diff options
Diffstat (limited to 'gcc/config/powerpcspe/powerpcspe.md')
-rw-r--r-- | gcc/config/powerpcspe/powerpcspe.md | 14770 |
1 files changed, 14770 insertions, 0 deletions
diff --git a/gcc/config/powerpcspe/powerpcspe.md b/gcc/config/powerpcspe/powerpcspe.md new file mode 100644 index 00000000000..799d786edfe --- /dev/null +++ b/gcc/config/powerpcspe/powerpcspe.md @@ -0,0 +1,14770 @@ +;; Machine description for IBM RISC System 6000 (POWER) for GNU C compiler +;; Copyright (C) 1990-2017 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/>. + +;;- See file "rtl.def" for documentation on define_insn, match_*, et. al. + +;; +;; REGNOS +;; + +(define_constants + [(FIRST_GPR_REGNO 0) + (STACK_POINTER_REGNUM 1) + (TOC_REGNUM 2) + (STATIC_CHAIN_REGNUM 11) + (HARD_FRAME_POINTER_REGNUM 31) + (LAST_GPR_REGNO 31) + (FIRST_FPR_REGNO 32) + (LAST_FPR_REGNO 63) + (LR_REGNO 65) + (CTR_REGNO 66) + (ARG_POINTER_REGNUM 67) + (CR0_REGNO 68) + (CR1_REGNO 69) + (CR2_REGNO 70) + (CR3_REGNO 71) + (CR4_REGNO 72) + (CR5_REGNO 73) + (CR6_REGNO 74) + (CR7_REGNO 75) + (MAX_CR_REGNO 75) + (CA_REGNO 76) + (FIRST_ALTIVEC_REGNO 77) + (LAST_ALTIVEC_REGNO 108) + (VRSAVE_REGNO 109) + (VSCR_REGNO 110) + (SPE_ACC_REGNO 111) + (SPEFSCR_REGNO 112) + (FRAME_POINTER_REGNUM 113) + (TFHAR_REGNO 114) + (TFIAR_REGNO 115) + (TEXASR_REGNO 116) + (FIRST_SPE_HIGH_REGNO 117) + (LAST_SPE_HIGH_REGNO 148) + ]) + +;; +;; UNSPEC usage +;; + +(define_c_enum "unspec" + [UNSPEC_FRSP ; frsp for POWER machines + UNSPEC_PROBE_STACK ; probe stack memory reference + UNSPEC_TOCPTR ; address of a word pointing to the TOC + UNSPEC_TOC ; address of the TOC (more-or-less) + UNSPEC_TOCSLOT ; offset from r1 of toc pointer save slot + UNSPEC_MOVSI_GOT + UNSPEC_MV_CR_OV ; move_from_CR_ov_bit + UNSPEC_FCTIWZ + UNSPEC_FRIM + UNSPEC_FRIN + UNSPEC_FRIP + UNSPEC_FRIZ + UNSPEC_XSRDPI + UNSPEC_LD_MPIC ; load_macho_picbase + UNSPEC_RELD_MPIC ; re-load_macho_picbase + UNSPEC_MPIC_CORRECT ; macho_correct_pic + UNSPEC_TLSGD + UNSPEC_TLSLD + UNSPEC_MOVESI_FROM_CR + UNSPEC_MOVESI_TO_CR + UNSPEC_TLSDTPREL + UNSPEC_TLSDTPRELHA + UNSPEC_TLSDTPRELLO + UNSPEC_TLSGOTDTPREL + UNSPEC_TLSTPREL + UNSPEC_TLSTPRELHA + UNSPEC_TLSTPRELLO + UNSPEC_TLSGOTTPREL + UNSPEC_TLSTLS + UNSPEC_FIX_TRUNC_TF ; fadd, rounding towards zero + UNSPEC_MV_CR_GT ; move_from_CR_gt_bit + UNSPEC_STFIWX + UNSPEC_POPCNTB + UNSPEC_FRES + UNSPEC_SP_SET + UNSPEC_SP_TEST + UNSPEC_SYNC + UNSPEC_LWSYNC + UNSPEC_SYNC_OP + UNSPEC_ATOMIC + UNSPEC_CMPXCHG + UNSPEC_XCHG + UNSPEC_AND + UNSPEC_DLMZB + UNSPEC_DLMZB_CR + UNSPEC_DLMZB_STRLEN + UNSPEC_RSQRT + UNSPEC_TOCREL + UNSPEC_MACHOPIC_OFFSET + UNSPEC_BPERM + UNSPEC_COPYSIGN + UNSPEC_PARITY + UNSPEC_CMPB + UNSPEC_FCTIW + UNSPEC_FCTID + UNSPEC_LFIWAX + UNSPEC_LFIWZX + UNSPEC_FCTIWUZ + UNSPEC_NOP + UNSPEC_GRP_END_NOP + UNSPEC_P8V_FMRGOW + UNSPEC_P8V_MTVSRWZ + UNSPEC_P8V_RELOAD_FROM_GPR + UNSPEC_P8V_MTVSRD + UNSPEC_P8V_XXPERMDI + UNSPEC_P8V_RELOAD_FROM_VSX + UNSPEC_ADDG6S + UNSPEC_CDTBCD + UNSPEC_CBCDTD + UNSPEC_DIVE + UNSPEC_DIVEO + UNSPEC_DIVEU + UNSPEC_DIVEUO + UNSPEC_UNPACK_128BIT + UNSPEC_PACK_128BIT + UNSPEC_LSQ + UNSPEC_FUSION_GPR + UNSPEC_STACK_CHECK + UNSPEC_FUSION_P9 + UNSPEC_FUSION_ADDIS + UNSPEC_ROUND_TO_ODD + UNSPEC_SIGNBIT + UNSPEC_SF_FROM_SI + UNSPEC_SI_FROM_SF + ]) + +;; +;; UNSPEC_VOLATILE usage +;; + +(define_c_enum "unspecv" + [UNSPECV_BLOCK + UNSPECV_LL ; load-locked + UNSPECV_SC ; store-conditional + UNSPECV_PROBE_STACK_RANGE ; probe range of stack addresses + UNSPECV_EH_RR ; eh_reg_restore + UNSPECV_ISYNC ; isync instruction + UNSPECV_MFTB ; move from time base + UNSPECV_NLGR ; non-local goto receiver + UNSPECV_MFFS ; Move from FPSCR + UNSPECV_MTFSF ; Move to FPSCR Fields + UNSPECV_SPLIT_STACK_RETURN ; A camouflaged return + ]) + + +;; Define an insn type attribute. This is used in function unit delay +;; computations. +(define_attr "type" + "integer,two,three, + add,logical,shift,insert, + mul,halfmul,div, + exts,cntlz,popcnt,isel, + load,store,fpload,fpstore,vecload,vecstore, + cmp, + branch,jmpreg,mfjmpr,mtjmpr,trap,isync,sync,load_l,store_c, + cr_logical,delayed_cr,mfcr,mfcrf,mtcr, + fpcompare,fp,fpsimple,dmul,sdiv,ddiv,ssqrt,dsqrt, + brinc, + vecsimple,veccomplex,vecdiv,veccmp,veccmpsimple,vecperm, + vecfloat,vecfdiv,vecdouble,mffgpr,mftgpr,crypto, + veclogical,veccmpfx,vecexts,vecmove, + htm,htmsimple,dfp" + (const_string "integer")) + +;; What data size does this instruction work on? +;; This is used for insert, mul and others as necessary. +(define_attr "size" "8,16,32,64,128" (const_string "32")) + +;; Is this instruction record form ("dot", signed compare to 0, writing CR0)? +;; This is used for add, logical, shift, exts, mul. +(define_attr "dot" "no,yes" (const_string "no")) + +;; Does this instruction sign-extend its result? +;; This is used for load insns. +(define_attr "sign_extend" "no,yes" (const_string "no")) + +;; Does this instruction use indexed (that is, reg+reg) addressing? +;; This is used for load and store insns. If operand 0 or 1 is a MEM +;; it is automatically set based on that. If a load or store instruction +;; has fewer than two operands it needs to set this attribute manually +;; or the compiler will crash. +(define_attr "indexed" "no,yes" + (if_then_else (ior (match_operand 0 "indexed_address_mem") + (match_operand 1 "indexed_address_mem")) + (const_string "yes") + (const_string "no"))) + +;; Does this instruction use update addressing? +;; This is used for load and store insns. See the comments for "indexed". +(define_attr "update" "no,yes" + (if_then_else (ior (match_operand 0 "update_address_mem") + (match_operand 1 "update_address_mem")) + (const_string "yes") + (const_string "no"))) + +;; Is this instruction using operands[2] as shift amount, and can that be a +;; register? +;; This is used for shift insns. +(define_attr "maybe_var_shift" "no,yes" (const_string "no")) + +;; Is this instruction using a shift amount from a register? +;; This is used for shift insns. +(define_attr "var_shift" "no,yes" + (if_then_else (and (eq_attr "type" "shift") + (eq_attr "maybe_var_shift" "yes")) + (if_then_else (match_operand 2 "gpc_reg_operand") + (const_string "yes") + (const_string "no")) + (const_string "no"))) + +;; Is copying of this instruction disallowed? +(define_attr "cannot_copy" "no,yes" (const_string "no")) + +;; Define floating point instruction sub-types for use with Xfpu.md +(define_attr "fp_type" "fp_default,fp_addsub_s,fp_addsub_d,fp_mul_s,fp_mul_d,fp_div_s,fp_div_d,fp_maddsub_s,fp_maddsub_d,fp_sqrt_s,fp_sqrt_d" (const_string "fp_default")) + +;; Length (in bytes). +; '(pc)' in the following doesn't include the instruction itself; it is +; calculated as if the instruction had zero size. +(define_attr "length" "" + (if_then_else (eq_attr "type" "branch") + (if_then_else (and (ge (minus (match_dup 0) (pc)) + (const_int -32768)) + (lt (minus (match_dup 0) (pc)) + (const_int 32764))) + (const_int 4) + (const_int 8)) + (const_int 4))) + +;; Processor type -- this attribute must exactly match the processor_type +;; enumeration in rs6000-opts.h. +(define_attr "cpu" + "ppc601,ppc603,ppc604,ppc604e,ppc620,ppc630, + ppc750,ppc7400,ppc7450, + ppc403,ppc405,ppc440,ppc476, + ppc8540,ppc8548,ppce300c2,ppce300c3,ppce500mc,ppce500mc64,ppce5500,ppce6500, + power4,power5,power6,power7,power8,power9, + rs64a,mpccore,cell,ppca2,titan" + (const (symbol_ref "rs6000_cpu_attr"))) + + +;; If this instruction is microcoded on the CELL processor +; The default for load extended, the recorded instructions and rotate/shifts by a variable is always microcoded +(define_attr "cell_micro" "not,conditional,always" + (if_then_else (ior (and (eq_attr "type" "shift,exts,mul") + (eq_attr "dot" "yes")) + (and (eq_attr "type" "load") + (eq_attr "sign_extend" "yes")) + (and (eq_attr "type" "shift") + (eq_attr "var_shift" "yes"))) + (const_string "always") + (const_string "not"))) + +(automata_option "ndfa") + +(include "rs64.md") +(include "mpc.md") +(include "40x.md") +(include "440.md") +(include "476.md") +(include "601.md") +(include "603.md") +(include "6xx.md") +(include "7xx.md") +(include "7450.md") +(include "8540.md") +(include "e300c2c3.md") +(include "e500mc.md") +(include "e500mc64.md") +(include "e5500.md") +(include "e6500.md") +(include "power4.md") +(include "power5.md") +(include "power6.md") +(include "power7.md") +(include "power8.md") +(include "power9.md") +(include "cell.md") +(include "xfpu.md") +(include "a2.md") +(include "titan.md") + +(include "predicates.md") +(include "constraints.md") + +(include "darwin.md") + + +;; Mode iterators + +; This mode iterator allows :GPR to be used to indicate the allowable size +; of whole values in GPRs. +(define_mode_iterator GPR [SI (DI "TARGET_POWERPC64")]) + +; Any supported integer mode. +(define_mode_iterator INT [QI HI SI DI TI PTI]) + +; Any supported integer mode that fits in one register. +(define_mode_iterator INT1 [QI HI SI (DI "TARGET_POWERPC64")]) + +; Integer modes supported in VSX registers with ISA 3.0 instructions +(define_mode_iterator INT_ISA3 [QI HI SI DI]) + +; Everything we can extend QImode to. +(define_mode_iterator EXTQI [SI (DI "TARGET_POWERPC64")]) + +; Everything we can extend HImode to. +(define_mode_iterator EXTHI [SI (DI "TARGET_POWERPC64")]) + +; Everything we can extend SImode to. +(define_mode_iterator EXTSI [(DI "TARGET_POWERPC64")]) + +; QImode or HImode for small integer moves and small atomic ops +(define_mode_iterator QHI [QI HI]) + +; QImode, HImode, SImode for fused ops only for GPR loads +(define_mode_iterator QHSI [QI HI SI]) + +; HImode or SImode for sign extended fusion ops +(define_mode_iterator HSI [HI SI]) + +; SImode or DImode, even if DImode doesn't fit in GPRs. +(define_mode_iterator SDI [SI DI]) + +; Types that can be fused with an ADDIS instruction to load or store a GPR +; register that has reg+offset addressing. +(define_mode_iterator GPR_FUSION [QI + HI + SI + (DI "TARGET_POWERPC64") + SF + (DF "TARGET_POWERPC64")]) + +; Types that can be fused with an ADDIS instruction to load or store a FPR +; register that has reg+offset addressing. +(define_mode_iterator FPR_FUSION [DI SF DF]) + +; The size of a pointer. Also, the size of the value that a record-condition +; (one with a '.') will compare; and the size used for arithmetic carries. +(define_mode_iterator P [(SI "TARGET_32BIT") (DI "TARGET_64BIT")]) + +; Iterator to add PTImode along with TImode (TImode can go in VSX registers, +; PTImode is GPR only) +(define_mode_iterator TI2 [TI PTI]) + +; Any hardware-supported floating-point mode +(define_mode_iterator FP [ + (SF "TARGET_HARD_FLOAT + && ((TARGET_FPRS && TARGET_SINGLE_FLOAT) || TARGET_E500_SINGLE)") + (DF "TARGET_HARD_FLOAT + && ((TARGET_FPRS && TARGET_DOUBLE_FLOAT) || TARGET_E500_DOUBLE)") + (TF "TARGET_HARD_FLOAT + && (TARGET_FPRS || TARGET_E500_DOUBLE) + && TARGET_LONG_DOUBLE_128") + (IF "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128") + (KF "TARGET_FLOAT128_TYPE") + (DD "TARGET_DFP") + (TD "TARGET_DFP")]) + +; Any fma capable floating-point mode. +(define_mode_iterator FMA_F [ + (SF "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT") + (DF "(TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT) + || VECTOR_UNIT_VSX_P (DFmode)") + (V2SF "TARGET_PAIRED_FLOAT") + (V4SF "VECTOR_UNIT_ALTIVEC_OR_VSX_P (V4SFmode)") + (V2DF "VECTOR_UNIT_ALTIVEC_OR_VSX_P (V2DFmode)") + (KF "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (KFmode)") + (TF "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (TFmode)") + ]) + +; Floating point move iterators to combine binary and decimal moves +(define_mode_iterator FMOVE32 [SF SD]) +(define_mode_iterator FMOVE64 [DF DD]) +(define_mode_iterator FMOVE64X [DI DF DD]) +(define_mode_iterator FMOVE128 [(TF "TARGET_LONG_DOUBLE_128") + (IF "FLOAT128_IBM_P (IFmode)") + (TD "TARGET_HARD_FLOAT && TARGET_FPRS")]) + +(define_mode_iterator FMOVE128_FPR [(TF "FLOAT128_2REG_P (TFmode)") + (IF "FLOAT128_2REG_P (IFmode)") + (TD "TARGET_HARD_FLOAT && TARGET_FPRS")]) + +; Iterators for 128 bit types for direct move +(define_mode_iterator FMOVE128_GPR [(TI "TARGET_VSX_TIMODE") + (V16QI "") + (V8HI "") + (V4SI "") + (V4SF "") + (V2DI "") + (V2DF "") + (V1TI "") + (KF "FLOAT128_VECTOR_P (KFmode)") + (TF "FLOAT128_VECTOR_P (TFmode)")]) + +; Iterator for 128-bit VSX types for pack/unpack +(define_mode_iterator FMOVE128_VSX [V1TI KF]) + +; Whether a floating point move is ok, don't allow SD without hardware FP +(define_mode_attr fmove_ok [(SF "") + (DF "") + (SD "TARGET_HARD_FLOAT && TARGET_FPRS") + (DD "")]) + +; Convert REAL_VALUE to the appropriate bits +(define_mode_attr real_value_to_target [(SF "REAL_VALUE_TO_TARGET_SINGLE") + (DF "REAL_VALUE_TO_TARGET_DOUBLE") + (SD "REAL_VALUE_TO_TARGET_DECIMAL32") + (DD "REAL_VALUE_TO_TARGET_DECIMAL64")]) + +; Whether 0.0 has an all-zero bit pattern +(define_mode_attr zero_fp [(SF "j") + (DF "j") + (TF "j") + (IF "j") + (KF "j") + (SD "wn") + (DD "wn") + (TD "wn")]) + +; Definitions for 64-bit VSX +(define_mode_attr f64_vsx [(DF "ws") (DD "wn")]) + +; Definitions for 64-bit direct move +(define_mode_attr f64_dm [(DF "wk") (DD "wh")]) + +; Definitions for 64-bit use of altivec registers +(define_mode_attr f64_av [(DF "wv") (DD "wn")]) + +; Definitions for 64-bit access to ISA 3.0 (power9) vector +(define_mode_attr f64_p9 [(DF "wb") (DD "wn")]) + +; These modes do not fit in integer registers in 32-bit mode. +; but on e500v2, the gpr are 64 bit registers +(define_mode_iterator DIFD [DI (DF "!TARGET_E500_DOUBLE") DD]) + +; Iterator for reciprocal estimate instructions +(define_mode_iterator RECIPF [SF DF V4SF V2DF]) + +; Iterator for just SF/DF +(define_mode_iterator SFDF [SF DF]) + +; Like SFDF, but a different name to match conditional move where the +; comparison operands may be a different mode than the input operands. +(define_mode_iterator SFDF2 [SF DF]) + +; Iterator for 128-bit floating point that uses the IBM double-double format +(define_mode_iterator IBM128 [(IF "FLOAT128_IBM_P (IFmode)") + (TF "FLOAT128_IBM_P (TFmode)")]) + +; Iterator for 128-bit floating point that uses IEEE 128-bit float +(define_mode_iterator IEEE128 [(KF "FLOAT128_IEEE_P (KFmode)") + (TF "FLOAT128_IEEE_P (TFmode)")]) + +; Iterator for 128-bit floating point +(define_mode_iterator FLOAT128 [(KF "TARGET_FLOAT128_TYPE") + (IF "TARGET_FLOAT128_TYPE") + (TF "TARGET_LONG_DOUBLE_128")]) + +; Iterator for signbit on 64-bit machines with direct move +(define_mode_iterator SIGNBIT [(KF "FLOAT128_VECTOR_P (KFmode)") + (TF "FLOAT128_VECTOR_P (TFmode)")]) + +; Iterator for ISA 3.0 supported floating point types +(define_mode_iterator FP_ISA3 [SF DF]) + +; SF/DF suffix for traditional floating instructions +(define_mode_attr Ftrad [(SF "s") (DF "")]) + +; SF/DF suffix for VSX instructions +(define_mode_attr Fvsx [(SF "sp") (DF "dp")]) + +; SF/DF constraint for arithmetic on traditional floating point registers +(define_mode_attr Ff [(SF "f") (DF "d") (DI "d")]) + +; SF/DF constraint for arithmetic on VSX registers using instructions added in +; ISA 2.06 (power7). This includes instructions that normally target DF mode, +; but are used on SFmode, since internally SFmode values are kept in the DFmode +; format. +(define_mode_attr Fv [(SF "ww") (DF "ws") (DI "wi")]) + +; SF/DF constraint for arithmetic on VSX registers. This is intended to be +; used for DFmode instructions added in ISA 2.06 (power7) and SFmode +; instructions added in ISA 2.07 (power8) +(define_mode_attr Fv2 [(SF "wy") (DF "ws") (DI "wi")]) + +; SF/DF constraint for arithmetic on altivec registers +(define_mode_attr Fa [(SF "wu") (DF "wv")]) + +; s/d suffix for things like fp_addsub_s/fp_addsub_d +(define_mode_attr Fs [(SF "s") (DF "d")]) + +; FRE/FRES support +(define_mode_attr Ffre [(SF "fres") (DF "fre")]) +(define_mode_attr FFRE [(SF "FRES") (DF "FRE")]) + +; Conditional returns. +(define_code_iterator any_return [return simple_return]) +(define_code_attr return_pred [(return "direct_return ()") + (simple_return "1")]) +(define_code_attr return_str [(return "") (simple_return "simple_")]) + +; Logical operators. +(define_code_iterator iorxor [ior xor]) +(define_code_iterator and_ior_xor [and ior xor]) + +; Signed/unsigned variants of ops. +(define_code_iterator any_extend [sign_extend zero_extend]) +(define_code_iterator any_fix [fix unsigned_fix]) +(define_code_iterator any_float [float unsigned_float]) + +(define_code_attr u [(sign_extend "") + (zero_extend "u") + (fix "") + (unsigned_fix "u")]) + +(define_code_attr su [(sign_extend "s") + (zero_extend "u") + (fix "s") + (unsigned_fix "s") + (float "s") + (unsigned_float "u")]) + +(define_code_attr az [(sign_extend "a") + (zero_extend "z") + (fix "a") + (unsigned_fix "z") + (float "a") + (unsigned_float "z")]) + +(define_code_attr uns [(fix "") + (unsigned_fix "uns") + (float "") + (unsigned_float "uns")]) + +; Various instructions that come in SI and DI forms. +; A generic w/d attribute, for things like cmpw/cmpd. +(define_mode_attr wd [(QI "b") + (HI "h") + (SI "w") + (DI "d") + (V16QI "b") + (V8HI "h") + (V4SI "w") + (V2DI "d") + (V1TI "q") + (TI "q")]) + +;; How many bits in this mode? +(define_mode_attr bits [(QI "8") (HI "16") (SI "32") (DI "64")]) + +; DImode bits +(define_mode_attr dbits [(QI "56") (HI "48") (SI "32")]) + +;; ISEL/ISEL64 target selection +(define_mode_attr sel [(SI "") (DI "64")]) + +;; Bitmask for shift instructions +(define_mode_attr hH [(SI "h") (DI "H")]) + +;; A mode twice the size of the given mode +(define_mode_attr dmode [(SI "di") (DI "ti")]) +(define_mode_attr DMODE [(SI "DI") (DI "TI")]) + +;; Suffix for reload patterns +(define_mode_attr ptrsize [(SI "32bit") + (DI "64bit")]) + +(define_mode_attr tptrsize [(SI "TARGET_32BIT") + (DI "TARGET_64BIT")]) + +(define_mode_attr mptrsize [(SI "si") + (DI "di")]) + +(define_mode_attr ptrload [(SI "lwz") + (DI "ld")]) + +(define_mode_attr ptrm [(SI "m") + (DI "Y")]) + +(define_mode_attr rreg [(SF "f") + (DF "ws") + (TF "f") + (TD "f") + (V4SF "wf") + (V2DF "wd")]) + +(define_mode_attr rreg2 [(SF "f") + (DF "d")]) + +(define_mode_attr SI_CONVERT_FP [(SF "TARGET_FCFIDS") + (DF "TARGET_FCFID")]) + +(define_mode_attr E500_CONVERT [(SF "!TARGET_FPRS") + (DF "TARGET_E500_DOUBLE")]) + +(define_mode_attr TARGET_FLOAT [(SF "TARGET_SINGLE_FLOAT") + (DF "TARGET_DOUBLE_FLOAT")]) + +;; Mode iterator for logical operations on 128-bit types +(define_mode_iterator BOOL_128 [TI + PTI + (V16QI "TARGET_ALTIVEC") + (V8HI "TARGET_ALTIVEC") + (V4SI "TARGET_ALTIVEC") + (V4SF "TARGET_ALTIVEC") + (V2DI "TARGET_ALTIVEC") + (V2DF "TARGET_ALTIVEC") + (V1TI "TARGET_ALTIVEC")]) + +;; For the GPRs we use 3 constraints for register outputs, two that are the +;; same as the output register, and a third where the output register is an +;; early clobber, so we don't have to deal with register overlaps. For the +;; vector types, we prefer to use the vector registers. For TI mode, allow +;; either. + +;; Mode attribute for boolean operation register constraints for output +(define_mode_attr BOOL_REGS_OUTPUT [(TI "&r,r,r,wt,v") + (PTI "&r,r,r") + (V16QI "wa,v,&?r,?r,?r") + (V8HI "wa,v,&?r,?r,?r") + (V4SI "wa,v,&?r,?r,?r") + (V4SF "wa,v,&?r,?r,?r") + (V2DI "wa,v,&?r,?r,?r") + (V2DF "wa,v,&?r,?r,?r") + (V1TI "wa,v,&?r,?r,?r")]) + +;; Mode attribute for boolean operation register constraints for operand1 +(define_mode_attr BOOL_REGS_OP1 [(TI "r,0,r,wt,v") + (PTI "r,0,r") + (V16QI "wa,v,r,0,r") + (V8HI "wa,v,r,0,r") + (V4SI "wa,v,r,0,r") + (V4SF "wa,v,r,0,r") + (V2DI "wa,v,r,0,r") + (V2DF "wa,v,r,0,r") + (V1TI "wa,v,r,0,r")]) + +;; Mode attribute for boolean operation register constraints for operand2 +(define_mode_attr BOOL_REGS_OP2 [(TI "r,r,0,wt,v") + (PTI "r,r,0") + (V16QI "wa,v,r,r,0") + (V8HI "wa,v,r,r,0") + (V4SI "wa,v,r,r,0") + (V4SF "wa,v,r,r,0") + (V2DI "wa,v,r,r,0") + (V2DF "wa,v,r,r,0") + (V1TI "wa,v,r,r,0")]) + +;; Mode attribute for boolean operation register constraints for operand1 +;; for one_cmpl. To simplify things, we repeat the constraint where 0 +;; is used for operand1 or operand2 +(define_mode_attr BOOL_REGS_UNARY [(TI "r,0,0,wt,v") + (PTI "r,0,0") + (V16QI "wa,v,r,0,0") + (V8HI "wa,v,r,0,0") + (V4SI "wa,v,r,0,0") + (V4SF "wa,v,r,0,0") + (V2DI "wa,v,r,0,0") + (V2DF "wa,v,r,0,0") + (V1TI "wa,v,r,0,0")]) + +;; Reload iterator for creating the function to allocate a base register to +;; supplement addressing modes. +(define_mode_iterator RELOAD [V16QI V8HI V4SI V2DI V4SF V2DF V1TI + SF SD SI DF DD DI TI PTI KF IF TF]) + +;; Iterate over smin, smax +(define_code_iterator fp_minmax [smin smax]) + +(define_code_attr minmax [(smin "min") + (smax "max")]) + +(define_code_attr SMINMAX [(smin "SMIN") + (smax "SMAX")]) + +;; Iterator to optimize the following cases: +;; D-form load to FPR register & move to Altivec register +;; Move Altivec register to FPR register and store +(define_mode_iterator ALTIVEC_DFORM [DI DF SF]) + + +;; Start with fixed-point load and store insns. Here we put only the more +;; complex forms. Basic data transfer is done later. + +(define_insn "zero_extendqi<mode>2" + [(set (match_operand:EXTQI 0 "gpc_reg_operand" "=r,r,^wJwK,^wK") + (zero_extend:EXTQI (match_operand:QI 1 "reg_or_mem_operand" "m,r,Z,wK")))] + "" + "@ + lbz%U1%X1 %0,%1 + rlwinm %0,%1,0,0xff + lxsibzx %x0,%y1 + vextractub %0,%1,7" + [(set_attr "type" "load,shift,fpload,vecperm")]) + +(define_insn_and_split "*zero_extendqi<mode>2_dot" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (zero_extend:EXTQI (match_operand:QI 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (clobber (match_scratch:EXTQI 0 "=r,r"))] + "rs6000_gen_cell_microcode" + "@ + andi. %0,%1,0xff + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (zero_extend:EXTQI (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*zero_extendqi<mode>2_dot2" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (zero_extend:EXTQI (match_operand:QI 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (set (match_operand:EXTQI 0 "gpc_reg_operand" "=r,r") + (zero_extend:EXTQI (match_dup 1)))] + "rs6000_gen_cell_microcode" + "@ + andi. %0,%1,0xff + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (zero_extend:EXTQI (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_insn "zero_extendhi<mode>2" + [(set (match_operand:EXTHI 0 "gpc_reg_operand" "=r,r,^wJwK,^wK") + (zero_extend:EXTHI (match_operand:HI 1 "reg_or_mem_operand" "m,r,Z,wK")))] + "" + "@ + lhz%U1%X1 %0,%1 + rlwinm %0,%1,0,0xffff + lxsihzx %x0,%y1 + vextractuh %0,%1,6" + [(set_attr "type" "load,shift,fpload,vecperm")]) + +(define_insn_and_split "*zero_extendhi<mode>2_dot" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (zero_extend:EXTHI (match_operand:HI 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (clobber (match_scratch:EXTHI 0 "=r,r"))] + "rs6000_gen_cell_microcode" + "@ + andi. %0,%1,0xffff + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (zero_extend:EXTHI (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*zero_extendhi<mode>2_dot2" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (zero_extend:EXTHI (match_operand:HI 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (set (match_operand:EXTHI 0 "gpc_reg_operand" "=r,r") + (zero_extend:EXTHI (match_dup 1)))] + "rs6000_gen_cell_microcode" + "@ + andi. %0,%1,0xffff + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (zero_extend:EXTHI (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_insn "zero_extendsi<mode>2" + [(set (match_operand:EXTSI 0 "gpc_reg_operand" "=r,r,wz,wu,wj,r,wJwK") + (zero_extend:EXTSI (match_operand:SI 1 "reg_or_mem_operand" "m,r,Z,Z,r,wIwH,wJwK")))] + "" + "@ + lwz%U1%X1 %0,%1 + rldicl %0,%1,0,32 + lfiwzx %0,%y1 + lxsiwzx %x0,%y1 + mtvsrwz %x0,%1 + mfvsrwz %0,%x1 + xxextractuw %x0,%x1,4" + [(set_attr "type" "load,shift,fpload,fpload,mffgpr,mftgpr,vecexts")]) + +(define_insn_and_split "*zero_extendsi<mode>2_dot" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (zero_extend:EXTSI (match_operand:SI 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (clobber (match_scratch:EXTSI 0 "=r,r"))] + "rs6000_gen_cell_microcode" + "@ + rldicl. %0,%1,0,32 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (zero_extend:DI (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*zero_extendsi<mode>2_dot2" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (zero_extend:EXTSI (match_operand:SI 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (set (match_operand:EXTSI 0 "gpc_reg_operand" "=r,r") + (zero_extend:EXTSI (match_dup 1)))] + "rs6000_gen_cell_microcode" + "@ + rldicl. %0,%1,0,32 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (zero_extend:EXTSI (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_insn "extendqi<mode>2" + [(set (match_operand:EXTQI 0 "gpc_reg_operand" "=r,?*wK") + (sign_extend:EXTQI (match_operand:QI 1 "gpc_reg_operand" "r,?*wK")))] + "" + "@ + extsb %0,%1 + vextsb2d %0,%1" + [(set_attr "type" "exts,vecperm")]) + +(define_insn_and_split "*extendqi<mode>2_dot" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (sign_extend:EXTQI (match_operand:QI 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (clobber (match_scratch:EXTQI 0 "=r,r"))] + "rs6000_gen_cell_microcode" + "@ + extsb. %0,%1 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (sign_extend:EXTQI (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "exts") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*extendqi<mode>2_dot2" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (sign_extend:EXTQI (match_operand:QI 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (set (match_operand:EXTQI 0 "gpc_reg_operand" "=r,r") + (sign_extend:EXTQI (match_dup 1)))] + "rs6000_gen_cell_microcode" + "@ + extsb. %0,%1 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (sign_extend:EXTQI (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "exts") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_expand "extendhi<mode>2" + [(set (match_operand:EXTHI 0 "gpc_reg_operand") + (sign_extend:EXTHI (match_operand:HI 1 "gpc_reg_operand")))] + "" + "") + +(define_insn "*extendhi<mode>2" + [(set (match_operand:EXTHI 0 "gpc_reg_operand" "=r,r,?*wK,?*wK") + (sign_extend:EXTHI (match_operand:HI 1 "reg_or_mem_operand" "m,r,Z,wK")))] + "rs6000_gen_cell_microcode || TARGET_VSX_SMALL_INTEGER" + "@ + lha%U1%X1 %0,%1 + extsh %0,%1 + # + vextsh2d %0,%1" + [(set_attr "type" "load,exts,fpload,vecperm") + (set_attr "sign_extend" "yes") + (set_attr "length" "4,4,8,4")]) + +(define_split + [(set (match_operand:EXTHI 0 "altivec_register_operand") + (sign_extend:EXTHI + (match_operand:HI 1 "indexed_or_indirect_operand")))] + "TARGET_P9_VECTOR && reload_completed" + [(set (match_dup 2) + (match_dup 1)) + (set (match_dup 0) + (sign_extend:EXTHI (match_dup 2)))] +{ + operands[2] = gen_rtx_REG (HImode, REGNO (operands[1])); +}) + +(define_insn "*extendhi<mode>2_noload" + [(set (match_operand:EXTHI 0 "gpc_reg_operand" "=r") + (sign_extend:EXTHI (match_operand:HI 1 "gpc_reg_operand" "r")))] + "!rs6000_gen_cell_microcode" + "extsh %0,%1" + [(set_attr "type" "exts")]) + +(define_insn_and_split "*extendhi<mode>2_dot" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (sign_extend:EXTHI (match_operand:HI 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (clobber (match_scratch:EXTHI 0 "=r,r"))] + "rs6000_gen_cell_microcode" + "@ + extsh. %0,%1 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (sign_extend:EXTHI (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "exts") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*extendhi<mode>2_dot2" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (sign_extend:EXTHI (match_operand:HI 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (set (match_operand:EXTHI 0 "gpc_reg_operand" "=r,r") + (sign_extend:EXTHI (match_dup 1)))] + "rs6000_gen_cell_microcode" + "@ + extsh. %0,%1 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (sign_extend:EXTHI (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "exts") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_insn "extendsi<mode>2" + [(set (match_operand:EXTSI 0 "gpc_reg_operand" "=r,r,wl,wu,wj,wK,wH") + (sign_extend:EXTSI (match_operand:SI 1 "lwa_operand" "Y,r,Z,Z,r,wK,wH")))] + "" + "@ + lwa%U1%X1 %0,%1 + extsw %0,%1 + lfiwax %0,%y1 + lxsiwax %x0,%y1 + mtvsrwa %x0,%1 + vextsw2d %0,%1 + #" + [(set_attr "type" "load,exts,fpload,fpload,mffgpr,vecexts,vecperm") + (set_attr "sign_extend" "yes") + (set_attr "length" "4,4,4,4,4,4,8")]) + +(define_split + [(set (match_operand:DI 0 "altivec_register_operand") + (sign_extend:DI (match_operand:SI 1 "altivec_register_operand")))] + "TARGET_VSX_SMALL_INTEGER && TARGET_P8_VECTOR && !TARGET_P9_VECTOR + && reload_completed" + [(const_int 0)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + int dest_regno = REGNO (dest); + int src_regno = REGNO (src); + rtx dest_v2di = gen_rtx_REG (V2DImode, dest_regno); + rtx src_v4si = gen_rtx_REG (V4SImode, src_regno); + + if (VECTOR_ELT_ORDER_BIG) + { + emit_insn (gen_altivec_vupkhsw (dest_v2di, src_v4si)); + emit_insn (gen_vsx_xxspltd_v2di (dest_v2di, dest_v2di, const1_rtx)); + } + else + { + emit_insn (gen_altivec_vupklsw (dest_v2di, src_v4si)); + emit_insn (gen_vsx_xxspltd_v2di (dest_v2di, dest_v2di, const0_rtx)); + } + DONE; +}) + +(define_insn_and_split "*extendsi<mode>2_dot" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (sign_extend:EXTSI (match_operand:SI 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (clobber (match_scratch:EXTSI 0 "=r,r"))] + "rs6000_gen_cell_microcode" + "@ + extsw. %0,%1 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (sign_extend:EXTSI (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "exts") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*extendsi<mode>2_dot2" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (sign_extend:EXTSI (match_operand:SI 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (set (match_operand:EXTSI 0 "gpc_reg_operand" "=r,r") + (sign_extend:EXTSI (match_dup 1)))] + "rs6000_gen_cell_microcode" + "@ + extsw. %0,%1 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (sign_extend:EXTSI (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "exts") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +;; IBM 405, 440, 464 and 476 half-word multiplication operations. + +(define_insn "*macchwc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (plus:SI (mult:SI (ashiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16)) + (sign_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "r"))) + (match_operand:SI 4 "gpc_reg_operand" "0")) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (mult:SI (ashiftrt:SI + (match_dup 2) + (const_int 16)) + (sign_extend:SI + (match_dup 1))) + (match_dup 4)))] + "TARGET_MULHW" + "macchw. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*macchw" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (mult:SI (ashiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16)) + (sign_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "r"))) + (match_operand:SI 3 "gpc_reg_operand" "0")))] + "TARGET_MULHW" + "macchw %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*macchwuc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (plus:SI (mult:SI (lshiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16)) + (zero_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "r"))) + (match_operand:SI 4 "gpc_reg_operand" "0")) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (mult:SI (lshiftrt:SI + (match_dup 2) + (const_int 16)) + (zero_extend:SI + (match_dup 1))) + (match_dup 4)))] + "TARGET_MULHW" + "macchwu. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*macchwu" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (mult:SI (lshiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16)) + (zero_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "r"))) + (match_operand:SI 3 "gpc_reg_operand" "0")))] + "TARGET_MULHW" + "macchwu %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*machhwc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (plus:SI (mult:SI (ashiftrt:SI + (match_operand:SI 1 "gpc_reg_operand" "%r") + (const_int 16)) + (ashiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16))) + (match_operand:SI 4 "gpc_reg_operand" "0")) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (mult:SI (ashiftrt:SI + (match_dup 1) + (const_int 16)) + (ashiftrt:SI + (match_dup 2) + (const_int 16))) + (match_dup 4)))] + "TARGET_MULHW" + "machhw. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*machhw" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (mult:SI (ashiftrt:SI + (match_operand:SI 1 "gpc_reg_operand" "%r") + (const_int 16)) + (ashiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16))) + (match_operand:SI 3 "gpc_reg_operand" "0")))] + "TARGET_MULHW" + "machhw %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*machhwuc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (plus:SI (mult:SI (lshiftrt:SI + (match_operand:SI 1 "gpc_reg_operand" "%r") + (const_int 16)) + (lshiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16))) + (match_operand:SI 4 "gpc_reg_operand" "0")) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (mult:SI (lshiftrt:SI + (match_dup 1) + (const_int 16)) + (lshiftrt:SI + (match_dup 2) + (const_int 16))) + (match_dup 4)))] + "TARGET_MULHW" + "machhwu. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*machhwu" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (mult:SI (lshiftrt:SI + (match_operand:SI 1 "gpc_reg_operand" "%r") + (const_int 16)) + (lshiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16))) + (match_operand:SI 3 "gpc_reg_operand" "0")))] + "TARGET_MULHW" + "machhwu %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*maclhwc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (plus:SI (mult:SI (sign_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "%r")) + (sign_extend:SI + (match_operand:HI 2 "gpc_reg_operand" "r"))) + (match_operand:SI 4 "gpc_reg_operand" "0")) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (mult:SI (sign_extend:SI + (match_dup 1)) + (sign_extend:SI + (match_dup 2))) + (match_dup 4)))] + "TARGET_MULHW" + "maclhw. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*maclhw" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (mult:SI (sign_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "%r")) + (sign_extend:SI + (match_operand:HI 2 "gpc_reg_operand" "r"))) + (match_operand:SI 3 "gpc_reg_operand" "0")))] + "TARGET_MULHW" + "maclhw %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*maclhwuc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (plus:SI (mult:SI (zero_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "%r")) + (zero_extend:SI + (match_operand:HI 2 "gpc_reg_operand" "r"))) + (match_operand:SI 4 "gpc_reg_operand" "0")) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (mult:SI (zero_extend:SI + (match_dup 1)) + (zero_extend:SI + (match_dup 2))) + (match_dup 4)))] + "TARGET_MULHW" + "maclhwu. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*maclhwu" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (mult:SI (zero_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "%r")) + (zero_extend:SI + (match_operand:HI 2 "gpc_reg_operand" "r"))) + (match_operand:SI 3 "gpc_reg_operand" "0")))] + "TARGET_MULHW" + "maclhwu %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*nmacchwc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (minus:SI (match_operand:SI 4 "gpc_reg_operand" "0") + (mult:SI (ashiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16)) + (sign_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "r")))) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (minus:SI (match_dup 4) + (mult:SI (ashiftrt:SI + (match_dup 2) + (const_int 16)) + (sign_extend:SI + (match_dup 1)))))] + "TARGET_MULHW" + "nmacchw. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*nmacchw" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (minus:SI (match_operand:SI 3 "gpc_reg_operand" "0") + (mult:SI (ashiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16)) + (sign_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "r")))))] + "TARGET_MULHW" + "nmacchw %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*nmachhwc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (minus:SI (match_operand:SI 4 "gpc_reg_operand" "0") + (mult:SI (ashiftrt:SI + (match_operand:SI 1 "gpc_reg_operand" "%r") + (const_int 16)) + (ashiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16)))) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (minus:SI (match_dup 4) + (mult:SI (ashiftrt:SI + (match_dup 1) + (const_int 16)) + (ashiftrt:SI + (match_dup 2) + (const_int 16)))))] + "TARGET_MULHW" + "nmachhw. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*nmachhw" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (minus:SI (match_operand:SI 3 "gpc_reg_operand" "0") + (mult:SI (ashiftrt:SI + (match_operand:SI 1 "gpc_reg_operand" "%r") + (const_int 16)) + (ashiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16)))))] + "TARGET_MULHW" + "nmachhw %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*nmaclhwc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (minus:SI (match_operand:SI 4 "gpc_reg_operand" "0") + (mult:SI (sign_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "%r")) + (sign_extend:SI + (match_operand:HI 2 "gpc_reg_operand" "r")))) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (minus:SI (match_dup 4) + (mult:SI (sign_extend:SI + (match_dup 1)) + (sign_extend:SI + (match_dup 2)))))] + "TARGET_MULHW" + "nmaclhw. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*nmaclhw" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (minus:SI (match_operand:SI 3 "gpc_reg_operand" "0") + (mult:SI (sign_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "%r")) + (sign_extend:SI + (match_operand:HI 2 "gpc_reg_operand" "r")))))] + "TARGET_MULHW" + "nmaclhw %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*mulchwc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (mult:SI (ashiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16)) + (sign_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "r"))) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mult:SI (ashiftrt:SI + (match_dup 2) + (const_int 16)) + (sign_extend:SI + (match_dup 1))))] + "TARGET_MULHW" + "mulchw. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*mulchw" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mult:SI (ashiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16)) + (sign_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "r"))))] + "TARGET_MULHW" + "mulchw %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*mulchwuc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (mult:SI (lshiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16)) + (zero_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "r"))) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mult:SI (lshiftrt:SI + (match_dup 2) + (const_int 16)) + (zero_extend:SI + (match_dup 1))))] + "TARGET_MULHW" + "mulchwu. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*mulchwu" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mult:SI (lshiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16)) + (zero_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "r"))))] + "TARGET_MULHW" + "mulchwu %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*mulhhwc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (mult:SI (ashiftrt:SI + (match_operand:SI 1 "gpc_reg_operand" "%r") + (const_int 16)) + (ashiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16))) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mult:SI (ashiftrt:SI + (match_dup 1) + (const_int 16)) + (ashiftrt:SI + (match_dup 2) + (const_int 16))))] + "TARGET_MULHW" + "mulhhw. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*mulhhw" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mult:SI (ashiftrt:SI + (match_operand:SI 1 "gpc_reg_operand" "%r") + (const_int 16)) + (ashiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16))))] + "TARGET_MULHW" + "mulhhw %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*mulhhwuc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (mult:SI (lshiftrt:SI + (match_operand:SI 1 "gpc_reg_operand" "%r") + (const_int 16)) + (lshiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16))) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mult:SI (lshiftrt:SI + (match_dup 1) + (const_int 16)) + (lshiftrt:SI + (match_dup 2) + (const_int 16))))] + "TARGET_MULHW" + "mulhhwu. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*mulhhwu" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mult:SI (lshiftrt:SI + (match_operand:SI 1 "gpc_reg_operand" "%r") + (const_int 16)) + (lshiftrt:SI + (match_operand:SI 2 "gpc_reg_operand" "r") + (const_int 16))))] + "TARGET_MULHW" + "mulhhwu %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*mullhwc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (mult:SI (sign_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "%r")) + (sign_extend:SI + (match_operand:HI 2 "gpc_reg_operand" "r"))) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mult:SI (sign_extend:SI + (match_dup 1)) + (sign_extend:SI + (match_dup 2))))] + "TARGET_MULHW" + "mullhw. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*mullhw" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mult:SI (sign_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "%r")) + (sign_extend:SI + (match_operand:HI 2 "gpc_reg_operand" "r"))))] + "TARGET_MULHW" + "mullhw %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*mullhwuc" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC (mult:SI (zero_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "%r")) + (zero_extend:SI + (match_operand:HI 2 "gpc_reg_operand" "r"))) + (const_int 0))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mult:SI (zero_extend:SI + (match_dup 1)) + (zero_extend:SI + (match_dup 2))))] + "TARGET_MULHW" + "mullhwu. %0,%1,%2" + [(set_attr "type" "halfmul")]) + +(define_insn "*mullhwu" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mult:SI (zero_extend:SI + (match_operand:HI 1 "gpc_reg_operand" "%r")) + (zero_extend:SI + (match_operand:HI 2 "gpc_reg_operand" "r"))))] + "TARGET_MULHW" + "mullhwu %0,%1,%2" + [(set_attr "type" "halfmul")]) + +;; IBM 405, 440, 464 and 476 string-search dlmzb instruction support. +(define_insn "dlmzb" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (unspec:CC [(match_operand:SI 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "gpc_reg_operand" "r")] + UNSPEC_DLMZB_CR)) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (unspec:SI [(match_dup 1) + (match_dup 2)] + UNSPEC_DLMZB))] + "TARGET_DLMZB" + "dlmzb. %0,%1,%2") + +(define_expand "strlensi" + [(set (match_operand:SI 0 "gpc_reg_operand" "") + (unspec:SI [(match_operand:BLK 1 "general_operand" "") + (match_operand:QI 2 "const_int_operand" "") + (match_operand 3 "const_int_operand" "")] + UNSPEC_DLMZB_STRLEN)) + (clobber (match_scratch:CC 4 "=x"))] + "TARGET_DLMZB && WORDS_BIG_ENDIAN && !optimize_size" +{ + rtx result = operands[0]; + rtx src = operands[1]; + rtx search_char = operands[2]; + rtx align = operands[3]; + rtx addr, scratch_string, word1, word2, scratch_dlmzb; + rtx loop_label, end_label, mem, cr0, cond; + if (search_char != const0_rtx + || GET_CODE (align) != CONST_INT + || INTVAL (align) < 8) + FAIL; + word1 = gen_reg_rtx (SImode); + word2 = gen_reg_rtx (SImode); + scratch_dlmzb = gen_reg_rtx (SImode); + scratch_string = gen_reg_rtx (Pmode); + loop_label = gen_label_rtx (); + end_label = gen_label_rtx (); + addr = force_reg (Pmode, XEXP (src, 0)); + emit_move_insn (scratch_string, addr); + emit_label (loop_label); + mem = change_address (src, SImode, scratch_string); + emit_move_insn (word1, mem); + emit_move_insn (word2, adjust_address (mem, SImode, 4)); + cr0 = gen_rtx_REG (CCmode, CR0_REGNO); + emit_insn (gen_dlmzb (scratch_dlmzb, word1, word2, cr0)); + cond = gen_rtx_NE (VOIDmode, cr0, const0_rtx); + emit_jump_insn (gen_rtx_SET (pc_rtx, + gen_rtx_IF_THEN_ELSE (VOIDmode, + cond, + gen_rtx_LABEL_REF + (VOIDmode, + end_label), + pc_rtx))); + emit_insn (gen_addsi3 (scratch_string, scratch_string, GEN_INT (8))); + emit_jump_insn (gen_rtx_SET (pc_rtx, + gen_rtx_LABEL_REF (VOIDmode, loop_label))); + emit_barrier (); + emit_label (end_label); + emit_insn (gen_addsi3 (scratch_string, scratch_string, scratch_dlmzb)); + emit_insn (gen_subsi3 (result, scratch_string, addr)); + emit_insn (gen_addsi3 (result, result, constm1_rtx)); + DONE; +}) + +;; Fixed-point arithmetic insns. + +(define_expand "add<mode>3" + [(set (match_operand:SDI 0 "gpc_reg_operand" "") + (plus:SDI (match_operand:SDI 1 "gpc_reg_operand" "") + (match_operand:SDI 2 "reg_or_add_cint_operand" "")))] + "" +{ + if (<MODE>mode == DImode && !TARGET_POWERPC64) + { + rtx lo0 = gen_lowpart (SImode, operands[0]); + rtx lo1 = gen_lowpart (SImode, operands[1]); + rtx lo2 = gen_lowpart (SImode, operands[2]); + rtx hi0 = gen_highpart (SImode, operands[0]); + rtx hi1 = gen_highpart (SImode, operands[1]); + rtx hi2 = gen_highpart_mode (SImode, DImode, operands[2]); + + if (!reg_or_short_operand (lo2, SImode)) + lo2 = force_reg (SImode, lo2); + if (!adde_operand (hi2, SImode)) + hi2 = force_reg (SImode, hi2); + + emit_insn (gen_addsi3_carry (lo0, lo1, lo2)); + emit_insn (gen_addsi3_carry_in (hi0, hi1, hi2)); + DONE; + } + + if (CONST_INT_P (operands[2]) && !add_operand (operands[2], <MODE>mode)) + { + rtx tmp = ((!can_create_pseudo_p () + || rtx_equal_p (operands[0], operands[1])) + ? operands[0] : gen_reg_rtx (<MODE>mode)); + + HOST_WIDE_INT val = INTVAL (operands[2]); + HOST_WIDE_INT low = ((val & 0xffff) ^ 0x8000) - 0x8000; + HOST_WIDE_INT rest = trunc_int_for_mode (val - low, <MODE>mode); + + if (<MODE>mode == DImode && !satisfies_constraint_L (GEN_INT (rest))) + FAIL; + + /* The ordering here is important for the prolog expander. + When space is allocated from the stack, adding 'low' first may + produce a temporary deallocation (which would be bad). */ + emit_insn (gen_add<mode>3 (tmp, operands[1], GEN_INT (rest))); + emit_insn (gen_add<mode>3 (operands[0], tmp, GEN_INT (low))); + DONE; + } +}) + +(define_insn "*add<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r,r,r") + (plus:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,b,b") + (match_operand:GPR 2 "add_operand" "r,I,L")))] + "" + "@ + add %0,%1,%2 + addi %0,%1,%2 + addis %0,%1,%v2" + [(set_attr "type" "add")]) + +(define_insn "addsi3_high" + [(set (match_operand:SI 0 "gpc_reg_operand" "=b") + (plus:SI (match_operand:SI 1 "gpc_reg_operand" "b") + (high:SI (match_operand 2 "" ""))))] + "TARGET_MACHO && !TARGET_64BIT" + "addis %0,%1,ha16(%2)" + [(set_attr "type" "add")]) + +(define_insn_and_split "*add<mode>3_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (plus:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:GPR 2 "gpc_reg_operand" "r,r")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "<MODE>mode == Pmode" + "@ + add. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (plus:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "add") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*add<mode>3_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (plus:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:GPR 2 "gpc_reg_operand" "r,r")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (plus:GPR (match_dup 1) + (match_dup 2)))] + "<MODE>mode == Pmode" + "@ + add. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (plus:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "add") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*add<mode>3_imm_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (plus:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,b") + (match_operand:GPR 2 "short_cint_operand" "I,I")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r")) + (clobber (reg:GPR CA_REGNO))] + "<MODE>mode == Pmode" + "@ + addic. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (plus:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "add") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*add<mode>3_imm_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (plus:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,b") + (match_operand:GPR 2 "short_cint_operand" "I,I")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (plus:GPR (match_dup 1) + (match_dup 2))) + (clobber (reg:GPR CA_REGNO))] + "<MODE>mode == Pmode" + "@ + addic. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (plus:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "add") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +;; Split an add that we can't do in one insn into two insns, each of which +;; does one 16-bit part. This is used by combine. Note that the low-order +;; add should be last in case the result gets used in an address. + +(define_split + [(set (match_operand:GPR 0 "gpc_reg_operand" "") + (plus:GPR (match_operand:GPR 1 "gpc_reg_operand" "") + (match_operand:GPR 2 "non_add_cint_operand" "")))] + "" + [(set (match_dup 0) (plus:GPR (match_dup 1) (match_dup 3))) + (set (match_dup 0) (plus:GPR (match_dup 0) (match_dup 4)))] +{ + HOST_WIDE_INT val = INTVAL (operands[2]); + HOST_WIDE_INT low = ((val & 0xffff) ^ 0x8000) - 0x8000; + HOST_WIDE_INT rest = trunc_int_for_mode (val - low, <MODE>mode); + + operands[4] = GEN_INT (low); + if (<MODE>mode == SImode || satisfies_constraint_L (GEN_INT (rest))) + operands[3] = GEN_INT (rest); + else if (can_create_pseudo_p ()) + { + operands[3] = gen_reg_rtx (DImode); + emit_move_insn (operands[3], operands[2]); + emit_insn (gen_adddi3 (operands[0], operands[1], operands[3])); + DONE; + } + else + FAIL; +}) + + +(define_insn "add<mode>3_carry" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (plus:P (match_operand:P 1 "gpc_reg_operand" "r") + (match_operand:P 2 "reg_or_short_operand" "rI"))) + (set (reg:P CA_REGNO) + (ltu:P (plus:P (match_dup 1) + (match_dup 2)) + (match_dup 1)))] + "" + "add%I2c %0,%1,%2" + [(set_attr "type" "add")]) + +(define_insn "*add<mode>3_imm_carry_pos" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (plus:P (match_operand:P 1 "gpc_reg_operand" "r") + (match_operand:P 2 "short_cint_operand" "n"))) + (set (reg:P CA_REGNO) + (geu:P (match_dup 1) + (match_operand:P 3 "const_int_operand" "n")))] + "INTVAL (operands[2]) > 0 + && INTVAL (operands[2]) + INTVAL (operands[3]) == 0" + "addic %0,%1,%2" + [(set_attr "type" "add")]) + +(define_insn "*add<mode>3_imm_carry_0" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (match_operand:P 1 "gpc_reg_operand" "r")) + (set (reg:P CA_REGNO) + (const_int 0))] + "" + "addic %0,%1,0" + [(set_attr "type" "add")]) + +(define_insn "*add<mode>3_imm_carry_m1" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (plus:P (match_operand:P 1 "gpc_reg_operand" "r") + (const_int -1))) + (set (reg:P CA_REGNO) + (ne:P (match_dup 1) + (const_int 0)))] + "" + "addic %0,%1,-1" + [(set_attr "type" "add")]) + +(define_insn "*add<mode>3_imm_carry_neg" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (plus:P (match_operand:P 1 "gpc_reg_operand" "r") + (match_operand:P 2 "short_cint_operand" "n"))) + (set (reg:P CA_REGNO) + (gtu:P (match_dup 1) + (match_operand:P 3 "const_int_operand" "n")))] + "INTVAL (operands[2]) < 0 + && INTVAL (operands[2]) + INTVAL (operands[3]) == -1" + "addic %0,%1,%2" + [(set_attr "type" "add")]) + + +(define_expand "add<mode>3_carry_in" + [(parallel [ + (set (match_operand:GPR 0 "gpc_reg_operand") + (plus:GPR (plus:GPR (match_operand:GPR 1 "gpc_reg_operand") + (match_operand:GPR 2 "adde_operand")) + (reg:GPR CA_REGNO))) + (clobber (reg:GPR CA_REGNO))])] + "" +{ + if (operands[2] == const0_rtx) + { + emit_insn (gen_add<mode>3_carry_in_0 (operands[0], operands[1])); + DONE; + } + if (operands[2] == constm1_rtx) + { + emit_insn (gen_add<mode>3_carry_in_m1 (operands[0], operands[1])); + DONE; + } +}) + +(define_insn "*add<mode>3_carry_in_internal" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (plus:GPR (plus:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "gpc_reg_operand" "r")) + (reg:GPR CA_REGNO))) + (clobber (reg:GPR CA_REGNO))] + "" + "adde %0,%1,%2" + [(set_attr "type" "add")]) + +(define_insn "add<mode>3_carry_in_0" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (plus:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (reg:GPR CA_REGNO))) + (clobber (reg:GPR CA_REGNO))] + "" + "addze %0,%1" + [(set_attr "type" "add")]) + +(define_insn "add<mode>3_carry_in_m1" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (plus:GPR (plus:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (reg:GPR CA_REGNO)) + (const_int -1))) + (clobber (reg:GPR CA_REGNO))] + "" + "addme %0,%1" + [(set_attr "type" "add")]) + + +(define_expand "one_cmpl<mode>2" + [(set (match_operand:SDI 0 "gpc_reg_operand" "") + (not:SDI (match_operand:SDI 1 "gpc_reg_operand" "")))] + "" +{ + if (<MODE>mode == DImode && !TARGET_POWERPC64) + { + rs6000_split_logical (operands, NOT, false, false, false); + DONE; + } +}) + +(define_insn "*one_cmpl<mode>2" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (not:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")))] + "" + "not %0,%1") + +(define_insn_and_split "*one_cmpl<mode>2_dot" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (not:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + not. %0,%1 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (not:GPR (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*one_cmpl<mode>2_dot2" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (not:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (not:GPR (match_dup 1)))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + not. %0,%1 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (not:GPR (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_expand "sub<mode>3" + [(set (match_operand:SDI 0 "gpc_reg_operand" "") + (minus:SDI (match_operand:SDI 1 "reg_or_short_operand" "") + (match_operand:SDI 2 "gpc_reg_operand" "")))] + "" +{ + if (<MODE>mode == DImode && !TARGET_POWERPC64) + { + rtx lo0 = gen_lowpart (SImode, operands[0]); + rtx lo1 = gen_lowpart (SImode, operands[1]); + rtx lo2 = gen_lowpart (SImode, operands[2]); + rtx hi0 = gen_highpart (SImode, operands[0]); + rtx hi1 = gen_highpart_mode (SImode, DImode, operands[1]); + rtx hi2 = gen_highpart (SImode, operands[2]); + + if (!reg_or_short_operand (lo1, SImode)) + lo1 = force_reg (SImode, lo1); + if (!adde_operand (hi1, SImode)) + hi1 = force_reg (SImode, hi1); + + emit_insn (gen_subfsi3_carry (lo0, lo2, lo1)); + emit_insn (gen_subfsi3_carry_in (hi0, hi2, hi1)); + DONE; + } + + if (short_cint_operand (operands[1], <MODE>mode)) + { + emit_insn (gen_subf<mode>3_imm (operands[0], operands[2], operands[1])); + DONE; + } +}) + +(define_insn "*subf<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (minus:GPR (match_operand:GPR 2 "gpc_reg_operand" "r") + (match_operand:GPR 1 "gpc_reg_operand" "r")))] + "" + "subf %0,%1,%2" + [(set_attr "type" "add")]) + +(define_insn_and_split "*subf<mode>3_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (minus:GPR (match_operand:GPR 2 "gpc_reg_operand" "r,r") + (match_operand:GPR 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "<MODE>mode == Pmode" + "@ + subf. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (minus:GPR (match_dup 2) + (match_dup 1))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "add") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*subf<mode>3_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (minus:GPR (match_operand:GPR 2 "gpc_reg_operand" "r,r") + (match_operand:GPR 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (minus:GPR (match_dup 2) + (match_dup 1)))] + "<MODE>mode == Pmode" + "@ + subf. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (minus:GPR (match_dup 2) + (match_dup 1))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "add") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn "subf<mode>3_imm" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (minus:GPR (match_operand:GPR 2 "short_cint_operand" "I") + (match_operand:GPR 1 "gpc_reg_operand" "r"))) + (clobber (reg:GPR CA_REGNO))] + "" + "subfic %0,%1,%2" + [(set_attr "type" "add")]) + +(define_insn_and_split "subf<mode>3_carry_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (minus:P (match_operand:P 2 "gpc_reg_operand" "r,r") + (match_operand:P 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (set (match_operand:P 0 "gpc_reg_operand" "=r,r") + (minus:P (match_dup 2) + (match_dup 1))) + (set (reg:P CA_REGNO) + (leu:P (match_dup 1) + (match_dup 2)))] + "<MODE>mode == Pmode" + "@ + subfc. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(parallel [(set (match_dup 0) + (minus:P (match_dup 2) + (match_dup 1))) + (set (reg:P CA_REGNO) + (leu:P (match_dup 1) + (match_dup 2)))]) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "add") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn "subf<mode>3_carry" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (minus:P (match_operand:P 2 "reg_or_short_operand" "rI") + (match_operand:P 1 "gpc_reg_operand" "r"))) + (set (reg:P CA_REGNO) + (leu:P (match_dup 1) + (match_dup 2)))] + "" + "subf%I2c %0,%1,%2" + [(set_attr "type" "add")]) + +(define_insn "*subf<mode>3_imm_carry_0" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (neg:P (match_operand:P 1 "gpc_reg_operand" "r"))) + (set (reg:P CA_REGNO) + (eq:P (match_dup 1) + (const_int 0)))] + "" + "subfic %0,%1,0" + [(set_attr "type" "add")]) + +(define_insn "*subf<mode>3_imm_carry_m1" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (not:P (match_operand:P 1 "gpc_reg_operand" "r"))) + (set (reg:P CA_REGNO) + (const_int 1))] + "" + "subfic %0,%1,-1" + [(set_attr "type" "add")]) + + +(define_expand "subf<mode>3_carry_in" + [(parallel [ + (set (match_operand:GPR 0 "gpc_reg_operand") + (plus:GPR (plus:GPR (not:GPR (match_operand:GPR 1 "gpc_reg_operand")) + (reg:GPR CA_REGNO)) + (match_operand:GPR 2 "adde_operand"))) + (clobber (reg:GPR CA_REGNO))])] + "" +{ + if (operands[2] == const0_rtx) + { + emit_insn (gen_subf<mode>3_carry_in_0 (operands[0], operands[1])); + DONE; + } + if (operands[2] == constm1_rtx) + { + emit_insn (gen_subf<mode>3_carry_in_m1 (operands[0], operands[1])); + DONE; + } +}) + +(define_insn "*subf<mode>3_carry_in_internal" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (plus:GPR (plus:GPR (not:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")) + (reg:GPR CA_REGNO)) + (match_operand:GPR 2 "gpc_reg_operand" "r"))) + (clobber (reg:GPR CA_REGNO))] + "" + "subfe %0,%1,%2" + [(set_attr "type" "add")]) + +(define_insn "subf<mode>3_carry_in_0" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (plus:GPR (not:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")) + (reg:GPR CA_REGNO))) + (clobber (reg:GPR CA_REGNO))] + "" + "subfze %0,%1" + [(set_attr "type" "add")]) + +(define_insn "subf<mode>3_carry_in_m1" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (plus:GPR (minus:GPR (reg:GPR CA_REGNO) + (match_operand:GPR 1 "gpc_reg_operand" "r")) + (const_int -2))) + (clobber (reg:GPR CA_REGNO))] + "" + "subfme %0,%1" + [(set_attr "type" "add")]) + +(define_insn "subf<mode>3_carry_in_xx" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (plus:GPR (reg:GPR CA_REGNO) + (const_int -1))) + (clobber (reg:GPR CA_REGNO))] + "" + "subfe %0,%0,%0" + [(set_attr "type" "add")]) + + +(define_insn "neg<mode>2" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (neg:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")))] + "" + "neg %0,%1" + [(set_attr "type" "add")]) + +(define_insn_and_split "*neg<mode>2_dot" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (neg:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "<MODE>mode == Pmode" + "@ + neg. %0,%1 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (neg:GPR (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "add") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*neg<mode>2_dot2" + [(set (match_operand:CC 2 "cc_reg_operand" "=x,?y") + (compare:CC (neg:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (neg:GPR (match_dup 1)))] + "<MODE>mode == Pmode" + "@ + neg. %0,%1 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[2], CCmode)" + [(set (match_dup 0) + (neg:GPR (match_dup 1))) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "add") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_insn "clz<mode>2" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (clz:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")))] + "" + "cntlz<wd> %0,%1" + [(set_attr "type" "cntlz")]) + +(define_expand "ctz<mode>2" + [(set (match_operand:GPR 0 "gpc_reg_operand") + (ctz:GPR (match_operand:GPR 1 "gpc_reg_operand")))] + "" +{ + if (TARGET_CTZ) + { + emit_insn (gen_ctz<mode>2_hw (operands[0], operands[1])); + DONE; + } + + rtx tmp1 = gen_reg_rtx (<MODE>mode); + rtx tmp2 = gen_reg_rtx (<MODE>mode); + rtx tmp3 = gen_reg_rtx (<MODE>mode); + + if (TARGET_POPCNTD) + { + emit_insn (gen_add<mode>3 (tmp1, operands[1], constm1_rtx)); + emit_insn (gen_one_cmpl<mode>2 (tmp2, operands[1])); + emit_insn (gen_and<mode>3 (tmp3, tmp1, tmp2)); + emit_insn (gen_popcntd<mode>2 (operands[0], tmp3)); + } + else + { + emit_insn (gen_neg<mode>2 (tmp1, operands[1])); + emit_insn (gen_and<mode>3 (tmp2, operands[1], tmp1)); + emit_insn (gen_clz<mode>2 (tmp3, tmp2)); + emit_insn (gen_sub<mode>3 (operands[0], GEN_INT (<bits> - 1), tmp3)); + } + + DONE; +}) + +(define_insn "ctz<mode>2_hw" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (ctz:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")))] + "TARGET_CTZ" + "cnttz<wd> %0,%1" + [(set_attr "type" "cntlz")]) + +(define_expand "ffs<mode>2" + [(set (match_operand:GPR 0 "gpc_reg_operand") + (ffs:GPR (match_operand:GPR 1 "gpc_reg_operand")))] + "" +{ + rtx tmp1 = gen_reg_rtx (<MODE>mode); + rtx tmp2 = gen_reg_rtx (<MODE>mode); + rtx tmp3 = gen_reg_rtx (<MODE>mode); + emit_insn (gen_neg<mode>2 (tmp1, operands[1])); + emit_insn (gen_and<mode>3 (tmp2, operands[1], tmp1)); + emit_insn (gen_clz<mode>2 (tmp3, tmp2)); + emit_insn (gen_sub<mode>3 (operands[0], GEN_INT (<bits>), tmp3)); + DONE; +}) + + +(define_expand "popcount<mode>2" + [(set (match_operand:GPR 0 "gpc_reg_operand" "") + (popcount:GPR (match_operand:GPR 1 "gpc_reg_operand" "")))] + "TARGET_POPCNTB || TARGET_POPCNTD" +{ + rs6000_emit_popcount (operands[0], operands[1]); + DONE; +}) + +(define_insn "popcntb<mode>2" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (unspec:GPR [(match_operand:GPR 1 "gpc_reg_operand" "r")] + UNSPEC_POPCNTB))] + "TARGET_POPCNTB" + "popcntb %0,%1" + [(set_attr "type" "popcnt")]) + +(define_insn "popcntd<mode>2" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (popcount:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")))] + "TARGET_POPCNTD" + "popcnt<wd> %0,%1" + [(set_attr "type" "popcnt")]) + + +(define_expand "parity<mode>2" + [(set (match_operand:GPR 0 "gpc_reg_operand" "") + (parity:GPR (match_operand:GPR 1 "gpc_reg_operand" "")))] + "TARGET_POPCNTB" +{ + rs6000_emit_parity (operands[0], operands[1]); + DONE; +}) + +(define_insn "parity<mode>2_cmpb" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (unspec:GPR [(match_operand:GPR 1 "gpc_reg_operand" "r")] UNSPEC_PARITY))] + "TARGET_CMPB && TARGET_POPCNTB" + "prty<wd> %0,%1" + [(set_attr "type" "popcnt")]) + +(define_insn "cmpb<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (unspec:GPR [(match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "gpc_reg_operand" "r")] UNSPEC_CMPB))] + "TARGET_CMPB" + "cmpb %0,%1,%2" + [(set_attr "type" "cmp")]) + +;; Since the hardware zeros the upper part of the register, save generating the +;; AND immediate if we are converting to unsigned +(define_insn "*bswap<mode>2_extenddi" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (zero_extend:DI + (bswap:HSI (match_operand:HSI 1 "memory_operand" "Z"))))] + "TARGET_POWERPC64" + "l<wd>brx %0,%y1" + [(set_attr "length" "4") + (set_attr "type" "load")]) + +(define_insn "*bswaphi2_extendsi" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (zero_extend:SI + (bswap:HI (match_operand:HI 1 "memory_operand" "Z"))))] + "" + "lhbrx %0,%y1" + [(set_attr "length" "4") + (set_attr "type" "load")]) + +;; Separate the bswap patterns into load, store, and gpr<-gpr. This prevents +;; the register allocator from converting a gpr<-gpr swap into a store and then +;; load with byte swap, which can be slower than doing it in the registers. It +;; also prevents certain failures with the RELOAD register allocator. + +(define_expand "bswap<mode>2" + [(use (match_operand:HSI 0 "reg_or_mem_operand")) + (use (match_operand:HSI 1 "reg_or_mem_operand"))] + "" +{ + rtx dest = operands[0]; + rtx src = operands[1]; + + if (!REG_P (dest) && !REG_P (src)) + src = force_reg (<MODE>mode, src); + + if (MEM_P (src)) + emit_insn (gen_bswap<mode>2_load (dest, src)); + else if (MEM_P (dest)) + emit_insn (gen_bswap<mode>2_store (dest, src)); + else + emit_insn (gen_bswap<mode>2_reg (dest, src)); + DONE; +}) + +(define_insn "bswap<mode>2_load" + [(set (match_operand:HSI 0 "gpc_reg_operand" "=r") + (bswap:HSI (match_operand:HSI 1 "memory_operand" "Z")))] + "" + "l<wd>brx %0,%y1" + [(set_attr "type" "load")]) + +(define_insn "bswap<mode>2_store" + [(set (match_operand:HSI 0 "memory_operand" "=Z") + (bswap:HSI (match_operand:HSI 1 "gpc_reg_operand" "r")))] + "" + "st<wd>brx %1,%y0" + [(set_attr "type" "store")]) + +(define_insn_and_split "bswaphi2_reg" + [(set (match_operand:HI 0 "gpc_reg_operand" "=&r") + (bswap:HI + (match_operand:HI 1 "gpc_reg_operand" "r"))) + (clobber (match_scratch:SI 2 "=&r"))] + "" + "#" + "reload_completed" + [(set (match_dup 3) + (and:SI (lshiftrt:SI (match_dup 4) + (const_int 8)) + (const_int 255))) + (set (match_dup 2) + (and:SI (ashift:SI (match_dup 4) + (const_int 8)) + (const_int 65280))) ;; 0xff00 + (set (match_dup 3) + (ior:SI (match_dup 3) + (match_dup 2)))] +{ + operands[3] = simplify_gen_subreg (SImode, operands[0], HImode, 0); + operands[4] = simplify_gen_subreg (SImode, operands[1], HImode, 0); +} + [(set_attr "length" "12") + (set_attr "type" "*")]) + +;; We are always BITS_BIG_ENDIAN, so the bit positions below in +;; zero_extract insns do not change for -mlittle. +(define_insn_and_split "bswapsi2_reg" + [(set (match_operand:SI 0 "gpc_reg_operand" "=&r") + (bswap:SI + (match_operand:SI 1 "gpc_reg_operand" "r")))] + "" + "#" + "reload_completed" + [(set (match_dup 0) ; DABC + (rotate:SI (match_dup 1) + (const_int 24))) + (set (match_dup 0) ; DCBC + (ior:SI (and:SI (ashift:SI (match_dup 1) + (const_int 8)) + (const_int 16711680)) + (and:SI (match_dup 0) + (const_int -16711681)))) + (set (match_dup 0) ; DCBA + (ior:SI (and:SI (lshiftrt:SI (match_dup 1) + (const_int 24)) + (const_int 255)) + (and:SI (match_dup 0) + (const_int -256))))] + "") + +;; On systems with LDBRX/STDBRX generate the loads/stores directly, just like +;; we do for L{H,W}BRX and ST{H,W}BRX above. If not, we have to generate more +;; complex code. + +(define_expand "bswapdi2" + [(parallel [(set (match_operand:DI 0 "reg_or_mem_operand" "") + (bswap:DI + (match_operand:DI 1 "reg_or_mem_operand" ""))) + (clobber (match_scratch:DI 2 "")) + (clobber (match_scratch:DI 3 ""))])] + "" +{ + rtx dest = operands[0]; + rtx src = operands[1]; + + if (!REG_P (dest) && !REG_P (src)) + operands[1] = src = force_reg (DImode, src); + + if (TARGET_POWERPC64 && TARGET_LDBRX) + { + if (MEM_P (src)) + emit_insn (gen_bswapdi2_load (dest, src)); + else if (MEM_P (dest)) + emit_insn (gen_bswapdi2_store (dest, src)); + else + emit_insn (gen_bswapdi2_reg (dest, src)); + DONE; + } + + if (!TARGET_POWERPC64) + { + /* 32-bit mode needs fewer scratch registers, but 32-bit addressing mode + that uses 64-bit registers needs the same scratch registers as 64-bit + mode. */ + emit_insn (gen_bswapdi2_32bit (dest, src)); + DONE; + } +}) + +;; Power7/cell has ldbrx/stdbrx, so use it directly +(define_insn "bswapdi2_load" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (bswap:DI (match_operand:DI 1 "memory_operand" "Z")))] + "TARGET_POWERPC64 && TARGET_LDBRX" + "ldbrx %0,%y1" + [(set_attr "type" "load")]) + +(define_insn "bswapdi2_store" + [(set (match_operand:DI 0 "memory_operand" "=Z") + (bswap:DI (match_operand:DI 1 "gpc_reg_operand" "r")))] + "TARGET_POWERPC64 && TARGET_LDBRX" + "stdbrx %1,%y0" + [(set_attr "type" "store")]) + +(define_insn "bswapdi2_reg" + [(set (match_operand:DI 0 "gpc_reg_operand" "=&r") + (bswap:DI (match_operand:DI 1 "gpc_reg_operand" "r"))) + (clobber (match_scratch:DI 2 "=&r")) + (clobber (match_scratch:DI 3 "=&r"))] + "TARGET_POWERPC64 && TARGET_LDBRX" + "#" + [(set_attr "length" "36")]) + +;; Non-power7/cell, fall back to use lwbrx/stwbrx +(define_insn "*bswapdi2_64bit" + [(set (match_operand:DI 0 "reg_or_mem_operand" "=r,Z,&r") + (bswap:DI (match_operand:DI 1 "reg_or_mem_operand" "Z,r,r"))) + (clobber (match_scratch:DI 2 "=&b,&b,&r")) + (clobber (match_scratch:DI 3 "=&r,&r,&r"))] + "TARGET_POWERPC64 && !TARGET_LDBRX + && (REG_P (operands[0]) || REG_P (operands[1])) + && !(MEM_P (operands[0]) && MEM_VOLATILE_P (operands[0])) + && !(MEM_P (operands[1]) && MEM_VOLATILE_P (operands[1]))" + "#" + [(set_attr "length" "16,12,36")]) + +(define_split + [(set (match_operand:DI 0 "gpc_reg_operand" "") + (bswap:DI (match_operand:DI 1 "indexed_or_indirect_operand" ""))) + (clobber (match_operand:DI 2 "gpc_reg_operand" "")) + (clobber (match_operand:DI 3 "gpc_reg_operand" ""))] + "TARGET_POWERPC64 && !TARGET_LDBRX && reload_completed" + [(const_int 0)] + " +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx op2 = operands[2]; + rtx op3 = operands[3]; + rtx op3_32 = simplify_gen_subreg (SImode, op3, DImode, + BYTES_BIG_ENDIAN ? 4 : 0); + rtx dest_32 = simplify_gen_subreg (SImode, dest, DImode, + BYTES_BIG_ENDIAN ? 4 : 0); + rtx addr1; + rtx addr2; + rtx word1; + rtx word2; + + addr1 = XEXP (src, 0); + if (GET_CODE (addr1) == PLUS) + { + emit_insn (gen_add3_insn (op2, XEXP (addr1, 0), GEN_INT (4))); + if (TARGET_AVOID_XFORM) + { + emit_insn (gen_add3_insn (op2, XEXP (addr1, 1), op2)); + addr2 = op2; + } + else + addr2 = gen_rtx_PLUS (Pmode, op2, XEXP (addr1, 1)); + } + else if (TARGET_AVOID_XFORM) + { + emit_insn (gen_add3_insn (op2, addr1, GEN_INT (4))); + addr2 = op2; + } + else + { + emit_move_insn (op2, GEN_INT (4)); + addr2 = gen_rtx_PLUS (Pmode, op2, addr1); + } + + word1 = change_address (src, SImode, addr1); + word2 = change_address (src, SImode, addr2); + + if (BYTES_BIG_ENDIAN) + { + emit_insn (gen_bswapsi2 (op3_32, word2)); + emit_insn (gen_bswapsi2 (dest_32, word1)); + } + else + { + emit_insn (gen_bswapsi2 (op3_32, word1)); + emit_insn (gen_bswapsi2 (dest_32, word2)); + } + + emit_insn (gen_ashldi3 (op3, op3, GEN_INT (32))); + emit_insn (gen_iordi3 (dest, dest, op3)); + DONE; +}") + +(define_split + [(set (match_operand:DI 0 "indexed_or_indirect_operand" "") + (bswap:DI (match_operand:DI 1 "gpc_reg_operand" ""))) + (clobber (match_operand:DI 2 "gpc_reg_operand" "")) + (clobber (match_operand:DI 3 "gpc_reg_operand" ""))] + "TARGET_POWERPC64 && !TARGET_LDBRX && reload_completed" + [(const_int 0)] + " +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx op2 = operands[2]; + rtx op3 = operands[3]; + rtx src_si = simplify_gen_subreg (SImode, src, DImode, + BYTES_BIG_ENDIAN ? 4 : 0); + rtx op3_si = simplify_gen_subreg (SImode, op3, DImode, + BYTES_BIG_ENDIAN ? 4 : 0); + rtx addr1; + rtx addr2; + rtx word1; + rtx word2; + + addr1 = XEXP (dest, 0); + if (GET_CODE (addr1) == PLUS) + { + emit_insn (gen_add3_insn (op2, XEXP (addr1, 0), GEN_INT (4))); + if (TARGET_AVOID_XFORM) + { + emit_insn (gen_add3_insn (op2, XEXP (addr1, 1), op2)); + addr2 = op2; + } + else + addr2 = gen_rtx_PLUS (Pmode, op2, XEXP (addr1, 1)); + } + else if (TARGET_AVOID_XFORM) + { + emit_insn (gen_add3_insn (op2, addr1, GEN_INT (4))); + addr2 = op2; + } + else + { + emit_move_insn (op2, GEN_INT (4)); + addr2 = gen_rtx_PLUS (Pmode, op2, addr1); + } + + word1 = change_address (dest, SImode, addr1); + word2 = change_address (dest, SImode, addr2); + + emit_insn (gen_lshrdi3 (op3, src, GEN_INT (32))); + + if (BYTES_BIG_ENDIAN) + { + emit_insn (gen_bswapsi2 (word1, src_si)); + emit_insn (gen_bswapsi2 (word2, op3_si)); + } + else + { + emit_insn (gen_bswapsi2 (word2, src_si)); + emit_insn (gen_bswapsi2 (word1, op3_si)); + } + DONE; +}") + +(define_split + [(set (match_operand:DI 0 "gpc_reg_operand" "") + (bswap:DI (match_operand:DI 1 "gpc_reg_operand" ""))) + (clobber (match_operand:DI 2 "gpc_reg_operand" "")) + (clobber (match_operand:DI 3 "gpc_reg_operand" ""))] + "TARGET_POWERPC64 && reload_completed" + [(const_int 0)] + " +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx op2 = operands[2]; + rtx op3 = operands[3]; + int lo_off = BYTES_BIG_ENDIAN ? 4 : 0; + rtx dest_si = simplify_gen_subreg (SImode, dest, DImode, lo_off); + rtx src_si = simplify_gen_subreg (SImode, src, DImode, lo_off); + rtx op2_si = simplify_gen_subreg (SImode, op2, DImode, lo_off); + rtx op3_si = simplify_gen_subreg (SImode, op3, DImode, lo_off); + + emit_insn (gen_lshrdi3 (op2, src, GEN_INT (32))); + emit_insn (gen_bswapsi2 (dest_si, src_si)); + emit_insn (gen_bswapsi2 (op3_si, op2_si)); + emit_insn (gen_ashldi3 (dest, dest, GEN_INT (32))); + emit_insn (gen_iordi3 (dest, dest, op3)); + DONE; +}") + +(define_insn "bswapdi2_32bit" + [(set (match_operand:DI 0 "reg_or_mem_operand" "=r,Z,?&r") + (bswap:DI (match_operand:DI 1 "reg_or_mem_operand" "Z,r,r"))) + (clobber (match_scratch:SI 2 "=&b,&b,X"))] + "!TARGET_POWERPC64 && (REG_P (operands[0]) || REG_P (operands[1]))" + "#" + [(set_attr "length" "16,12,36")]) + +(define_split + [(set (match_operand:DI 0 "gpc_reg_operand" "") + (bswap:DI (match_operand:DI 1 "indexed_or_indirect_operand" ""))) + (clobber (match_operand:SI 2 "gpc_reg_operand" ""))] + "!TARGET_POWERPC64 && reload_completed" + [(const_int 0)] + " +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx op2 = operands[2]; + rtx dest1 = simplify_gen_subreg (SImode, dest, DImode, 0); + rtx dest2 = simplify_gen_subreg (SImode, dest, DImode, 4); + rtx addr1; + rtx addr2; + rtx word1; + rtx word2; + + addr1 = XEXP (src, 0); + if (GET_CODE (addr1) == PLUS) + { + emit_insn (gen_add3_insn (op2, XEXP (addr1, 0), GEN_INT (4))); + if (TARGET_AVOID_XFORM + || REGNO (XEXP (addr1, 1)) == REGNO (dest2)) + { + emit_insn (gen_add3_insn (op2, XEXP (addr1, 1), op2)); + addr2 = op2; + } + else + addr2 = gen_rtx_PLUS (SImode, op2, XEXP (addr1, 1)); + } + else if (TARGET_AVOID_XFORM + || REGNO (addr1) == REGNO (dest2)) + { + emit_insn (gen_add3_insn (op2, addr1, GEN_INT (4))); + addr2 = op2; + } + else + { + emit_move_insn (op2, GEN_INT (4)); + addr2 = gen_rtx_PLUS (SImode, op2, addr1); + } + + word1 = change_address (src, SImode, addr1); + word2 = change_address (src, SImode, addr2); + + emit_insn (gen_bswapsi2 (dest2, word1)); + /* The REGNO (dest2) tests above ensure that addr2 has not been trashed, + thus allowing us to omit an early clobber on the output. */ + emit_insn (gen_bswapsi2 (dest1, word2)); + DONE; +}") + +(define_split + [(set (match_operand:DI 0 "indexed_or_indirect_operand" "") + (bswap:DI (match_operand:DI 1 "gpc_reg_operand" ""))) + (clobber (match_operand:SI 2 "gpc_reg_operand" ""))] + "!TARGET_POWERPC64 && reload_completed" + [(const_int 0)] + " +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx op2 = operands[2]; + rtx src1 = simplify_gen_subreg (SImode, src, DImode, 0); + rtx src2 = simplify_gen_subreg (SImode, src, DImode, 4); + rtx addr1; + rtx addr2; + rtx word1; + rtx word2; + + addr1 = XEXP (dest, 0); + if (GET_CODE (addr1) == PLUS) + { + emit_insn (gen_add3_insn (op2, XEXP (addr1, 0), GEN_INT (4))); + if (TARGET_AVOID_XFORM) + { + emit_insn (gen_add3_insn (op2, XEXP (addr1, 1), op2)); + addr2 = op2; + } + else + addr2 = gen_rtx_PLUS (SImode, op2, XEXP (addr1, 1)); + } + else if (TARGET_AVOID_XFORM) + { + emit_insn (gen_add3_insn (op2, addr1, GEN_INT (4))); + addr2 = op2; + } + else + { + emit_move_insn (op2, GEN_INT (4)); + addr2 = gen_rtx_PLUS (SImode, op2, addr1); + } + + word1 = change_address (dest, SImode, addr1); + word2 = change_address (dest, SImode, addr2); + + emit_insn (gen_bswapsi2 (word2, src1)); + emit_insn (gen_bswapsi2 (word1, src2)); + DONE; +}") + +(define_split + [(set (match_operand:DI 0 "gpc_reg_operand" "") + (bswap:DI (match_operand:DI 1 "gpc_reg_operand" ""))) + (clobber (match_operand:SI 2 "" ""))] + "!TARGET_POWERPC64 && reload_completed" + [(const_int 0)] + " +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx src1 = simplify_gen_subreg (SImode, src, DImode, 0); + rtx src2 = simplify_gen_subreg (SImode, src, DImode, 4); + rtx dest1 = simplify_gen_subreg (SImode, dest, DImode, 0); + rtx dest2 = simplify_gen_subreg (SImode, dest, DImode, 4); + + emit_insn (gen_bswapsi2 (dest1, src2)); + emit_insn (gen_bswapsi2 (dest2, src1)); + DONE; +}") + + +(define_insn "mul<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (mult:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,r") + (match_operand:GPR 2 "reg_or_short_operand" "r,I")))] + "" + "@ + mull<wd> %0,%1,%2 + mulli %0,%1,%2" + [(set_attr "type" "mul") + (set (attr "size") + (cond [(match_operand:GPR 2 "s8bit_cint_operand" "") + (const_string "8") + (match_operand:GPR 2 "short_cint_operand" "") + (const_string "16")] + (const_string "<bits>")))]) + +(define_insn_and_split "*mul<mode>3_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (mult:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:GPR 2 "gpc_reg_operand" "r,r")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + mull<wd>. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (mult:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "mul") + (set_attr "size" "<bits>") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*mul<mode>3_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (mult:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:GPR 2 "gpc_reg_operand" "r,r")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (mult:GPR (match_dup 1) + (match_dup 2)))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + mull<wd>. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (mult:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "mul") + (set_attr "size" "<bits>") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_expand "<su>mul<mode>3_highpart" + [(set (match_operand:GPR 0 "gpc_reg_operand") + (subreg:GPR + (mult:<DMODE> (any_extend:<DMODE> + (match_operand:GPR 1 "gpc_reg_operand")) + (any_extend:<DMODE> + (match_operand:GPR 2 "gpc_reg_operand"))) + 0))] + "" +{ + if (<MODE>mode == SImode && TARGET_POWERPC64) + { + emit_insn (gen_<su>mulsi3_highpart_64 (operands[0], operands[1], + operands[2])); + DONE; + } + + if (!WORDS_BIG_ENDIAN) + { + emit_insn (gen_<su>mul<mode>3_highpart_le (operands[0], operands[1], + operands[2])); + DONE; + } +}) + +(define_insn "*<su>mul<mode>3_highpart" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (subreg:GPR + (mult:<DMODE> (any_extend:<DMODE> + (match_operand:GPR 1 "gpc_reg_operand" "r")) + (any_extend:<DMODE> + (match_operand:GPR 2 "gpc_reg_operand" "r"))) + 0))] + "WORDS_BIG_ENDIAN && !(<MODE>mode == SImode && TARGET_POWERPC64)" + "mulh<wd><u> %0,%1,%2" + [(set_attr "type" "mul") + (set_attr "size" "<bits>")]) + +(define_insn "<su>mulsi3_highpart_le" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (subreg:SI + (mult:DI (any_extend:DI + (match_operand:SI 1 "gpc_reg_operand" "r")) + (any_extend:DI + (match_operand:SI 2 "gpc_reg_operand" "r"))) + 4))] + "!WORDS_BIG_ENDIAN && !TARGET_POWERPC64" + "mulhw<u> %0,%1,%2" + [(set_attr "type" "mul")]) + +(define_insn "<su>muldi3_highpart_le" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (subreg:DI + (mult:TI (any_extend:TI + (match_operand:DI 1 "gpc_reg_operand" "r")) + (any_extend:TI + (match_operand:DI 2 "gpc_reg_operand" "r"))) + 8))] + "!WORDS_BIG_ENDIAN && TARGET_POWERPC64" + "mulhd<u> %0,%1,%2" + [(set_attr "type" "mul") + (set_attr "size" "64")]) + +(define_insn "<su>mulsi3_highpart_64" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (truncate:SI + (lshiftrt:DI + (mult:DI (any_extend:DI + (match_operand:SI 1 "gpc_reg_operand" "r")) + (any_extend:DI + (match_operand:SI 2 "gpc_reg_operand" "r"))) + (const_int 32))))] + "TARGET_POWERPC64" + "mulhw<u> %0,%1,%2" + [(set_attr "type" "mul")]) + +(define_expand "<u>mul<mode><dmode>3" + [(set (match_operand:<DMODE> 0 "gpc_reg_operand") + (mult:<DMODE> (any_extend:<DMODE> + (match_operand:GPR 1 "gpc_reg_operand")) + (any_extend:<DMODE> + (match_operand:GPR 2 "gpc_reg_operand"))))] + "!(<MODE>mode == SImode && TARGET_POWERPC64)" +{ + rtx l = gen_reg_rtx (<MODE>mode); + rtx h = gen_reg_rtx (<MODE>mode); + emit_insn (gen_mul<mode>3 (l, operands[1], operands[2])); + emit_insn (gen_<su>mul<mode>3_highpart (h, operands[1], operands[2])); + emit_move_insn (gen_lowpart (<MODE>mode, operands[0]), l); + emit_move_insn (gen_highpart (<MODE>mode, operands[0]), h); + DONE; +}) + +(define_insn "*maddld4" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (plus:DI (mult:DI (match_operand:DI 1 "gpc_reg_operand" "r") + (match_operand:DI 2 "gpc_reg_operand" "r")) + (match_operand:DI 3 "gpc_reg_operand" "r")))] + "TARGET_MADDLD" + "maddld %0,%1,%2,%3" + [(set_attr "type" "mul")]) + +(define_insn "udiv<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (udiv:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "gpc_reg_operand" "r")))] + "" + "div<wd>u %0,%1,%2" + [(set_attr "type" "div") + (set_attr "size" "<bits>")]) + + +;; For powers of two we can do sra[wd]i/addze for divide and then adjust for +;; modulus. If it isn't a power of two, force operands into register and do +;; a normal divide. +(define_expand "div<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "") + (div:GPR (match_operand:GPR 1 "gpc_reg_operand" "") + (match_operand:GPR 2 "reg_or_cint_operand" "")))] + "" +{ + if (CONST_INT_P (operands[2]) + && INTVAL (operands[2]) > 0 + && exact_log2 (INTVAL (operands[2])) >= 0) + { + emit_insn (gen_div<mode>3_sra (operands[0], operands[1], operands[2])); + DONE; + } + + operands[2] = force_reg (<MODE>mode, operands[2]); +}) + +(define_insn "*div<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (div:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "gpc_reg_operand" "r")))] + "" + "div<wd> %0,%1,%2" + [(set_attr "type" "div") + (set_attr "size" "<bits>")]) + +(define_insn "div<mode>3_sra" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (div:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "exact_log2_cint_operand" "N"))) + (clobber (reg:GPR CA_REGNO))] + "" + "sra<wd>i %0,%1,%p2\;addze %0,%0" + [(set_attr "type" "two") + (set_attr "length" "8")]) + +(define_insn_and_split "*div<mode>3_sra_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (div:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:GPR 2 "exact_log2_cint_operand" "N,N")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r")) + (clobber (reg:GPR CA_REGNO))] + "<MODE>mode == Pmode" + "@ + sra<wd>i %0,%1,%p2\;addze. %0,%0 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(parallel [(set (match_dup 0) + (div:GPR (match_dup 1) + (match_dup 2))) + (clobber (reg:GPR CA_REGNO))]) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "two") + (set_attr "length" "8,12") + (set_attr "cell_micro" "not")]) + +(define_insn_and_split "*div<mode>3_sra_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (div:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:GPR 2 "exact_log2_cint_operand" "N,N")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (div:GPR (match_dup 1) + (match_dup 2))) + (clobber (reg:GPR CA_REGNO))] + "<MODE>mode == Pmode" + "@ + sra<wd>i %0,%1,%p2\;addze. %0,%0 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(parallel [(set (match_dup 0) + (div:GPR (match_dup 1) + (match_dup 2))) + (clobber (reg:GPR CA_REGNO))]) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "two") + (set_attr "length" "8,12") + (set_attr "cell_micro" "not")]) + +(define_expand "mod<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand") + (mod:GPR (match_operand:GPR 1 "gpc_reg_operand") + (match_operand:GPR 2 "reg_or_cint_operand")))] + "" +{ + int i; + rtx temp1; + rtx temp2; + + if (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) <= 0 + || (i = exact_log2 (INTVAL (operands[2]))) < 0) + { + if (!TARGET_MODULO) + FAIL; + + operands[2] = force_reg (<MODE>mode, operands[2]); + } + else + { + temp1 = gen_reg_rtx (<MODE>mode); + temp2 = gen_reg_rtx (<MODE>mode); + + emit_insn (gen_div<mode>3 (temp1, operands[1], operands[2])); + emit_insn (gen_ashl<mode>3 (temp2, temp1, GEN_INT (i))); + emit_insn (gen_sub<mode>3 (operands[0], operands[1], temp2)); + DONE; + } +}) + +;; In order to enable using a peephole2 for combining div/mod to eliminate the +;; mod, prefer putting the result of mod into a different register +(define_insn "*mod<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=&r") + (mod:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "gpc_reg_operand" "r")))] + "TARGET_MODULO" + "mods<wd> %0,%1,%2" + [(set_attr "type" "div") + (set_attr "size" "<bits>")]) + + +(define_insn "umod<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=&r") + (umod:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "gpc_reg_operand" "r")))] + "TARGET_MODULO" + "modu<wd> %0,%1,%2" + [(set_attr "type" "div") + (set_attr "size" "<bits>")]) + +;; On machines with modulo support, do a combined div/mod the old fashioned +;; method, since the multiply/subtract is faster than doing the mod instruction +;; after a divide. + +(define_peephole2 + [(set (match_operand:GPR 0 "gpc_reg_operand" "") + (div:GPR (match_operand:GPR 1 "gpc_reg_operand" "") + (match_operand:GPR 2 "gpc_reg_operand" ""))) + (set (match_operand:GPR 3 "gpc_reg_operand" "") + (mod:GPR (match_dup 1) + (match_dup 2)))] + "TARGET_MODULO + && ! reg_mentioned_p (operands[0], operands[1]) + && ! reg_mentioned_p (operands[0], operands[2]) + && ! reg_mentioned_p (operands[3], operands[1]) + && ! reg_mentioned_p (operands[3], operands[2])" + [(set (match_dup 0) + (div:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (mult:GPR (match_dup 0) + (match_dup 2))) + (set (match_dup 3) + (minus:GPR (match_dup 1) + (match_dup 3)))]) + +(define_peephole2 + [(set (match_operand:GPR 0 "gpc_reg_operand" "") + (udiv:GPR (match_operand:GPR 1 "gpc_reg_operand" "") + (match_operand:GPR 2 "gpc_reg_operand" ""))) + (set (match_operand:GPR 3 "gpc_reg_operand" "") + (umod:GPR (match_dup 1) + (match_dup 2)))] + "TARGET_MODULO + && ! reg_mentioned_p (operands[0], operands[1]) + && ! reg_mentioned_p (operands[0], operands[2]) + && ! reg_mentioned_p (operands[3], operands[1]) + && ! reg_mentioned_p (operands[3], operands[2])" + [(set (match_dup 0) + (udiv:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (mult:GPR (match_dup 0) + (match_dup 2))) + (set (match_dup 3) + (minus:GPR (match_dup 1) + (match_dup 3)))]) + + +;; Logical instructions +;; The logical instructions are mostly combined by using match_operator, +;; but the plain AND insns are somewhat different because there is no +;; plain 'andi' (only 'andi.'), no plain 'andis', and there are all +;; those rotate-and-mask operations. Thus, the AND insns come first. + +(define_expand "and<mode>3" + [(set (match_operand:SDI 0 "gpc_reg_operand" "") + (and:SDI (match_operand:SDI 1 "gpc_reg_operand" "") + (match_operand:SDI 2 "reg_or_cint_operand" "")))] + "" +{ + if (<MODE>mode == DImode && !TARGET_POWERPC64) + { + rs6000_split_logical (operands, AND, false, false, false); + DONE; + } + + if (CONST_INT_P (operands[2])) + { + if (rs6000_is_valid_and_mask (operands[2], <MODE>mode)) + { + emit_insn (gen_and<mode>3_mask (operands[0], operands[1], operands[2])); + DONE; + } + + if (logical_const_operand (operands[2], <MODE>mode) + && rs6000_gen_cell_microcode) + { + emit_insn (gen_and<mode>3_imm (operands[0], operands[1], operands[2])); + DONE; + } + + if (rs6000_is_valid_2insn_and (operands[2], <MODE>mode)) + { + rs6000_emit_2insn_and (<MODE>mode, operands, true, 0); + DONE; + } + + operands[2] = force_reg (<MODE>mode, operands[2]); + } +}) + + +(define_insn "and<mode>3_imm" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (and:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r") + (match_operand:GPR 2 "logical_const_operand" "n"))) + (clobber (match_scratch:CC 3 "=x"))] + "rs6000_gen_cell_microcode + && !rs6000_is_valid_and_mask (operands[2], <MODE>mode)" + "andi%e2. %0,%1,%u2" + [(set_attr "type" "logical") + (set_attr "dot" "yes")]) + +(define_insn_and_split "*and<mode>3_imm_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,??y") + (compare:CC (and:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,r") + (match_operand:GPR 2 "logical_const_operand" "n,n")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r")) + (clobber (match_scratch:CC 4 "=X,x"))] + "(<MODE>mode == Pmode || UINTVAL (operands[2]) <= 0x7fffffff) + && rs6000_gen_cell_microcode + && !rs6000_is_valid_and_mask (operands[2], <MODE>mode)" + "@ + andi%e2. %0,%1,%u2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(parallel [(set (match_dup 0) + (and:GPR (match_dup 1) + (match_dup 2))) + (clobber (match_dup 4))]) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*and<mode>3_imm_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,??y") + (compare:CC (and:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,r") + (match_operand:GPR 2 "logical_const_operand" "n,n")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (and:GPR (match_dup 1) + (match_dup 2))) + (clobber (match_scratch:CC 4 "=X,x"))] + "(<MODE>mode == Pmode || UINTVAL (operands[2]) <= 0x7fffffff) + && rs6000_gen_cell_microcode + && !rs6000_is_valid_and_mask (operands[2], <MODE>mode)" + "@ + andi%e2. %0,%1,%u2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(parallel [(set (match_dup 0) + (and:GPR (match_dup 1) + (match_dup 2))) + (clobber (match_dup 4))]) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*and<mode>3_imm_mask_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,??y") + (compare:CC (and:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,r") + (match_operand:GPR 2 "logical_const_operand" "n,n")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "(<MODE>mode == Pmode || UINTVAL (operands[2]) <= 0x7fffffff) + && rs6000_gen_cell_microcode + && rs6000_is_valid_and_mask (operands[2], <MODE>mode)" + "@ + andi%e2. %0,%1,%u2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (and:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*and<mode>3_imm_mask_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,??y") + (compare:CC (and:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,r") + (match_operand:GPR 2 "logical_const_operand" "n,n")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (and:GPR (match_dup 1) + (match_dup 2)))] + "(<MODE>mode == Pmode || UINTVAL (operands[2]) <= 0x7fffffff) + && rs6000_gen_cell_microcode + && rs6000_is_valid_and_mask (operands[2], <MODE>mode)" + "@ + andi%e2. %0,%1,%u2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (and:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn "*and<mode>3_imm_dot_shifted" + [(set (match_operand:CC 3 "cc_reg_operand" "=x") + (compare:CC + (and:GPR + (lshiftrt:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r") + (match_operand:SI 4 "const_int_operand" "n")) + (match_operand:GPR 2 "const_int_operand" "n")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r"))] + "logical_const_operand (GEN_INT (UINTVAL (operands[2]) + << INTVAL (operands[4])), + DImode) + && (<MODE>mode == Pmode + || (UINTVAL (operands[2]) << INTVAL (operands[4])) <= 0x7fffffff) + && rs6000_gen_cell_microcode" +{ + operands[2] = GEN_INT (UINTVAL (operands[2]) << INTVAL (operands[4])); + return "andi%e2. %0,%1,%u2"; +} + [(set_attr "type" "logical") + (set_attr "dot" "yes")]) + + +(define_insn "and<mode>3_mask" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (and:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r") + (match_operand:GPR 2 "const_int_operand" "n")))] + "rs6000_is_valid_and_mask (operands[2], <MODE>mode)" +{ + return rs6000_insn_for_and_mask (<MODE>mode, operands, false); +} + [(set_attr "type" "shift")]) + +(define_insn_and_split "*and<mode>3_mask_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (and:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,r") + (match_operand:GPR 2 "const_int_operand" "n,n")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "(<MODE>mode == Pmode || UINTVAL (operands[2]) <= 0x7fffffff) + && rs6000_gen_cell_microcode + && !logical_const_operand (operands[2], <MODE>mode) + && rs6000_is_valid_and_mask (operands[2], <MODE>mode)" +{ + if (which_alternative == 0) + return rs6000_insn_for_and_mask (<MODE>mode, operands, true); + else + return "#"; +} + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (and:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*and<mode>3_mask_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (and:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,r") + (match_operand:GPR 2 "const_int_operand" "n,n")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (and:GPR (match_dup 1) + (match_dup 2)))] + "(<MODE>mode == Pmode || UINTVAL (operands[2]) <= 0x7fffffff) + && rs6000_gen_cell_microcode + && !logical_const_operand (operands[2], <MODE>mode) + && rs6000_is_valid_and_mask (operands[2], <MODE>mode)" +{ + if (which_alternative == 0) + return rs6000_insn_for_and_mask (<MODE>mode, operands, true); + else + return "#"; +} + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (and:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_insn_and_split "*and<mode>3_2insn" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (and:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r") + (match_operand:GPR 2 "const_int_operand" "n")))] + "rs6000_is_valid_2insn_and (operands[2], <MODE>mode) + && !(rs6000_is_valid_and_mask (operands[2], <MODE>mode) + || (logical_const_operand (operands[2], <MODE>mode) + && rs6000_gen_cell_microcode))" + "#" + "&& 1" + [(pc)] +{ + rs6000_emit_2insn_and (<MODE>mode, operands, false, 0); + DONE; +} + [(set_attr "type" "shift") + (set_attr "length" "8")]) + +(define_insn_and_split "*and<mode>3_2insn_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (and:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,r") + (match_operand:GPR 2 "const_int_operand" "n,n")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "(<MODE>mode == Pmode || UINTVAL (operands[2]) <= 0x7fffffff) + && rs6000_gen_cell_microcode + && rs6000_is_valid_2insn_and (operands[2], <MODE>mode) + && !(rs6000_is_valid_and_mask (operands[2], <MODE>mode) + || (logical_const_operand (operands[2], <MODE>mode) + && rs6000_gen_cell_microcode))" + "#" + "&& reload_completed" + [(pc)] +{ + rs6000_emit_2insn_and (<MODE>mode, operands, false, 1); + DONE; +} + [(set_attr "type" "shift") + (set_attr "dot" "yes") + (set_attr "length" "8,12")]) + +(define_insn_and_split "*and<mode>3_2insn_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (and:GPR (match_operand:GPR 1 "gpc_reg_operand" "%r,r") + (match_operand:GPR 2 "const_int_operand" "n,n")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (and:GPR (match_dup 1) + (match_dup 2)))] + "(<MODE>mode == Pmode || UINTVAL (operands[2]) <= 0x7fffffff) + && rs6000_gen_cell_microcode + && rs6000_is_valid_2insn_and (operands[2], <MODE>mode) + && !(rs6000_is_valid_and_mask (operands[2], <MODE>mode) + || (logical_const_operand (operands[2], <MODE>mode) + && rs6000_gen_cell_microcode))" + "#" + "&& reload_completed" + [(pc)] +{ + rs6000_emit_2insn_and (<MODE>mode, operands, false, 2); + DONE; +} + [(set_attr "type" "shift") + (set_attr "dot" "yes") + (set_attr "length" "8,12")]) + + +(define_expand "<code><mode>3" + [(set (match_operand:SDI 0 "gpc_reg_operand" "") + (iorxor:SDI (match_operand:SDI 1 "gpc_reg_operand" "") + (match_operand:SDI 2 "reg_or_cint_operand" "")))] + "" +{ + if (<MODE>mode == DImode && !TARGET_POWERPC64) + { + rs6000_split_logical (operands, <CODE>, false, false, false); + DONE; + } + + if (non_logical_cint_operand (operands[2], <MODE>mode)) + { + rtx tmp = ((!can_create_pseudo_p () + || rtx_equal_p (operands[0], operands[1])) + ? operands[0] : gen_reg_rtx (<MODE>mode)); + + HOST_WIDE_INT value = INTVAL (operands[2]); + HOST_WIDE_INT lo = value & 0xffff; + HOST_WIDE_INT hi = value - lo; + + emit_insn (gen_<code><mode>3 (tmp, operands[1], GEN_INT (hi))); + emit_insn (gen_<code><mode>3 (operands[0], tmp, GEN_INT (lo))); + DONE; + } + + if (!reg_or_logical_cint_operand (operands[2], <MODE>mode)) + operands[2] = force_reg (<MODE>mode, operands[2]); +}) + +(define_split + [(set (match_operand:GPR 0 "gpc_reg_operand" "") + (iorxor:GPR (match_operand:GPR 1 "gpc_reg_operand" "") + (match_operand:GPR 2 "non_logical_cint_operand" "")))] + "" + [(set (match_dup 3) + (iorxor:GPR (match_dup 1) + (match_dup 4))) + (set (match_dup 0) + (iorxor:GPR (match_dup 3) + (match_dup 5)))] +{ + operands[3] = ((!can_create_pseudo_p () + || rtx_equal_p (operands[0], operands[1])) + ? operands[0] : gen_reg_rtx (<MODE>mode)); + + HOST_WIDE_INT value = INTVAL (operands[2]); + HOST_WIDE_INT lo = value & 0xffff; + HOST_WIDE_INT hi = value - lo; + + operands[4] = GEN_INT (hi); + operands[5] = GEN_INT (lo); +}) + +(define_insn "*bool<mode>3_imm" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (match_operator:GPR 3 "boolean_or_operator" + [(match_operand:GPR 1 "gpc_reg_operand" "%r") + (match_operand:GPR 2 "logical_const_operand" "n")]))] + "" + "%q3i%e2 %0,%1,%u2" + [(set_attr "type" "logical")]) + +(define_insn "*bool<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (match_operator:GPR 3 "boolean_operator" + [(match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "gpc_reg_operand" "r")]))] + "" + "%q3 %0,%1,%2" + [(set_attr "type" "logical")]) + +(define_insn_and_split "*bool<mode>3_dot" + [(set (match_operand:CC 4 "cc_reg_operand" "=x,?y") + (compare:CC (match_operator:GPR 3 "boolean_operator" + [(match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:GPR 2 "gpc_reg_operand" "r,r")]) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + %q3. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[4], CCmode)" + [(set (match_dup 0) + (match_dup 3)) + (set (match_dup 4) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*bool<mode>3_dot2" + [(set (match_operand:CC 4 "cc_reg_operand" "=x,?y") + (compare:CC (match_operator:GPR 3 "boolean_operator" + [(match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:GPR 2 "gpc_reg_operand" "r,r")]) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (match_dup 3))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + %q3. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[4], CCmode)" + [(set (match_dup 0) + (match_dup 3)) + (set (match_dup 4) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_insn "*boolc<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (match_operator:GPR 3 "boolean_operator" + [(not:GPR (match_operand:GPR 2 "gpc_reg_operand" "r")) + (match_operand:GPR 1 "gpc_reg_operand" "r")]))] + "" + "%q3 %0,%1,%2" + [(set_attr "type" "logical")]) + +(define_insn_and_split "*boolc<mode>3_dot" + [(set (match_operand:CC 4 "cc_reg_operand" "=x,?y") + (compare:CC (match_operator:GPR 3 "boolean_operator" + [(not:GPR (match_operand:GPR 2 "gpc_reg_operand" "r,r")) + (match_operand:GPR 1 "gpc_reg_operand" "r,r")]) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + %q3. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[4], CCmode)" + [(set (match_dup 0) + (match_dup 3)) + (set (match_dup 4) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*boolc<mode>3_dot2" + [(set (match_operand:CC 4 "cc_reg_operand" "=x,?y") + (compare:CC (match_operator:GPR 3 "boolean_operator" + [(not:GPR (match_operand:GPR 2 "gpc_reg_operand" "r,r")) + (match_operand:GPR 1 "gpc_reg_operand" "r,r")]) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (match_dup 3))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + %q3. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[4], CCmode)" + [(set (match_dup 0) + (match_dup 3)) + (set (match_dup 4) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_insn "*boolcc<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (match_operator:GPR 3 "boolean_operator" + [(not:GPR (match_operand:GPR 1 "gpc_reg_operand" "r")) + (not:GPR (match_operand:GPR 2 "gpc_reg_operand" "r"))]))] + "" + "%q3 %0,%1,%2" + [(set_attr "type" "logical")]) + +(define_insn_and_split "*boolcc<mode>3_dot" + [(set (match_operand:CC 4 "cc_reg_operand" "=x,?y") + (compare:CC (match_operator:GPR 3 "boolean_operator" + [(not:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r")) + (not:GPR (match_operand:GPR 2 "gpc_reg_operand" "r,r"))]) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + %q3. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[4], CCmode)" + [(set (match_dup 0) + (match_dup 3)) + (set (match_dup 4) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*boolcc<mode>3_dot2" + [(set (match_operand:CC 4 "cc_reg_operand" "=x,?y") + (compare:CC (match_operator:GPR 3 "boolean_operator" + [(not:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r")) + (not:GPR (match_operand:GPR 2 "gpc_reg_operand" "r,r"))]) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (match_dup 3))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + %q3. %0,%1,%2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[4], CCmode)" + [(set (match_dup 0) + (match_dup 3)) + (set (match_dup 4) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "logical") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +;; TODO: Should have dots of this as well. +(define_insn "*eqv<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (not:GPR (xor:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "gpc_reg_operand" "r"))))] + "" + "eqv %0,%1,%2" + [(set_attr "type" "logical")]) + +;; Rotate-and-mask and insert. + +(define_insn "*rotl<mode>3_mask" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (and:GPR (match_operator:GPR 4 "rotate_mask_operator" + [(match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "reg_or_cint_operand" "rn")]) + (match_operand:GPR 3 "const_int_operand" "n")))] + "rs6000_is_valid_shift_mask (operands[3], operands[4], <MODE>mode)" +{ + return rs6000_insn_for_shift_mask (<MODE>mode, operands, false); +} + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes")]) + +(define_insn_and_split "*rotl<mode>3_mask_dot" + [(set (match_operand:CC 5 "cc_reg_operand" "=x,?y") + (compare:CC + (and:GPR (match_operator:GPR 4 "rotate_mask_operator" + [(match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:SI 2 "reg_or_cint_operand" "rn,rn")]) + (match_operand:GPR 3 "const_int_operand" "n,n")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "(<MODE>mode == Pmode || UINTVAL (operands[3]) <= 0x7fffffff) + && rs6000_gen_cell_microcode + && rs6000_is_valid_shift_mask (operands[3], operands[4], <MODE>mode)" +{ + if (which_alternative == 0) + return rs6000_insn_for_shift_mask (<MODE>mode, operands, true); + else + return "#"; +} + "&& reload_completed && cc_reg_not_cr0_operand (operands[5], CCmode)" + [(set (match_dup 0) + (and:GPR (match_dup 4) + (match_dup 3))) + (set (match_dup 5) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*rotl<mode>3_mask_dot2" + [(set (match_operand:CC 5 "cc_reg_operand" "=x,?y") + (compare:CC + (and:GPR (match_operator:GPR 4 "rotate_mask_operator" + [(match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:SI 2 "reg_or_cint_operand" "rn,rn")]) + (match_operand:GPR 3 "const_int_operand" "n,n")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (and:GPR (match_dup 4) + (match_dup 3)))] + "(<MODE>mode == Pmode || UINTVAL (operands[3]) <= 0x7fffffff) + && rs6000_gen_cell_microcode + && rs6000_is_valid_shift_mask (operands[3], operands[4], <MODE>mode)" +{ + if (which_alternative == 0) + return rs6000_insn_for_shift_mask (<MODE>mode, operands, true); + else + return "#"; +} + "&& reload_completed && cc_reg_not_cr0_operand (operands[5], CCmode)" + [(set (match_dup 0) + (and:GPR (match_dup 4) + (match_dup 3))) + (set (match_dup 5) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +; Special case for less-than-0. We can do it with just one machine +; instruction, but the generic optimizers do not realise it is cheap. +(define_insn "*lt0_disi" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (lt:DI (match_operand:SI 1 "gpc_reg_operand" "r") + (const_int 0)))] + "TARGET_POWERPC64" + "rlwinm %0,%1,1,31,31" + [(set_attr "type" "shift")]) + + + +; Two forms for insert (the two arms of the IOR are not canonicalized, +; both are an AND so are the same precedence). +(define_insn "*rotl<mode>3_insert" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (ior:GPR (and:GPR (match_operator:GPR 4 "rotate_mask_operator" + [(match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "const_int_operand" "n")]) + (match_operand:GPR 3 "const_int_operand" "n")) + (and:GPR (match_operand:GPR 5 "gpc_reg_operand" "0") + (match_operand:GPR 6 "const_int_operand" "n"))))] + "rs6000_is_valid_insert_mask (operands[3], operands[4], <MODE>mode) + && UINTVAL (operands[3]) + UINTVAL (operands[6]) + 1 == 0" +{ + return rs6000_insn_for_insert_mask (<MODE>mode, operands, false); +} + [(set_attr "type" "insert")]) +; FIXME: this needs an attr "size", so that the scheduler can see the +; difference between rlwimi and rldimi. We also might want dot forms, +; but not for rlwimi on POWER4 and similar processors. + +(define_insn "*rotl<mode>3_insert_2" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (ior:GPR (and:GPR (match_operand:GPR 5 "gpc_reg_operand" "0") + (match_operand:GPR 6 "const_int_operand" "n")) + (and:GPR (match_operator:GPR 4 "rotate_mask_operator" + [(match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "const_int_operand" "n")]) + (match_operand:GPR 3 "const_int_operand" "n"))))] + "rs6000_is_valid_insert_mask (operands[3], operands[4], <MODE>mode) + && UINTVAL (operands[3]) + UINTVAL (operands[6]) + 1 == 0" +{ + return rs6000_insn_for_insert_mask (<MODE>mode, operands, false); +} + [(set_attr "type" "insert")]) + +; There are also some forms without one of the ANDs. +(define_insn "*rotl<mode>3_insert_3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (ior:GPR (and:GPR (match_operand:GPR 3 "gpc_reg_operand" "0") + (match_operand:GPR 4 "const_int_operand" "n")) + (ashift:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "const_int_operand" "n"))))] + "INTVAL (operands[2]) == exact_log2 (UINTVAL (operands[4]) + 1)" +{ + if (<MODE>mode == SImode) + return "rlwimi %0,%1,%h2,0,31-%h2"; + else + return "rldimi %0,%1,%H2,0"; +} + [(set_attr "type" "insert")]) + +(define_insn "*rotl<mode>3_insert_4" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (ior:GPR (and:GPR (match_operand:GPR 3 "gpc_reg_operand" "0") + (match_operand:GPR 4 "const_int_operand" "n")) + (lshiftrt:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "const_int_operand" "n"))))] + "<MODE>mode == SImode && + GET_MODE_PRECISION (<MODE>mode) + == INTVAL (operands[2]) + exact_log2 (-UINTVAL (operands[4]))" +{ + operands[2] = GEN_INT (GET_MODE_PRECISION (<MODE>mode) + - INTVAL (operands[2])); + if (<MODE>mode == SImode) + return "rlwimi %0,%1,%h2,32-%h2,31"; + else + return "rldimi %0,%1,%H2,64-%H2"; +} + [(set_attr "type" "insert")]) + +(define_insn "*rotlsi3_insert_5" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r,r") + (ior:SI (and:SI (match_operand:SI 1 "gpc_reg_operand" "0,r") + (match_operand:SI 2 "const_int_operand" "n,n")) + (and:SI (match_operand:SI 3 "gpc_reg_operand" "r,0") + (match_operand:SI 4 "const_int_operand" "n,n"))))] + "rs6000_is_valid_mask (operands[2], NULL, NULL, SImode) + && UINTVAL (operands[2]) != 0 && UINTVAL (operands[4]) != 0 + && UINTVAL (operands[2]) + UINTVAL (operands[4]) + 1 == 0" + "@ + rlwimi %0,%3,0,%4 + rlwimi %0,%1,0,%2" + [(set_attr "type" "insert")]) + +(define_insn "*rotldi3_insert_6" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (ior:DI (and:DI (match_operand:DI 1 "gpc_reg_operand" "0") + (match_operand:DI 2 "const_int_operand" "n")) + (and:DI (match_operand:DI 3 "gpc_reg_operand" "r") + (match_operand:DI 4 "const_int_operand" "n"))))] + "exact_log2 (-UINTVAL (operands[2])) > 0 + && UINTVAL (operands[2]) + UINTVAL (operands[4]) + 1 == 0" +{ + operands[5] = GEN_INT (64 - exact_log2 (-UINTVAL (operands[2]))); + return "rldimi %0,%3,0,%5"; +} + [(set_attr "type" "insert") + (set_attr "size" "64")]) + +(define_insn "*rotldi3_insert_7" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (ior:DI (and:DI (match_operand:DI 3 "gpc_reg_operand" "r") + (match_operand:DI 4 "const_int_operand" "n")) + (and:DI (match_operand:DI 1 "gpc_reg_operand" "0") + (match_operand:DI 2 "const_int_operand" "n"))))] + "exact_log2 (-UINTVAL (operands[2])) > 0 + && UINTVAL (operands[2]) + UINTVAL (operands[4]) + 1 == 0" +{ + operands[5] = GEN_INT (64 - exact_log2 (-UINTVAL (operands[2]))); + return "rldimi %0,%3,0,%5"; +} + [(set_attr "type" "insert") + (set_attr "size" "64")]) + + +; This handles the important case of multiple-precision shifts. There is +; no canonicalization rule for ASHIFT vs. LSHIFTRT, so two patterns. +(define_split + [(set (match_operand:GPR 0 "gpc_reg_operand") + (ior:GPR (ashift:GPR (match_operand:GPR 1 "gpc_reg_operand") + (match_operand:SI 3 "const_int_operand")) + (lshiftrt:GPR (match_operand:GPR 2 "gpc_reg_operand") + (match_operand:SI 4 "const_int_operand"))))] + "can_create_pseudo_p () + && INTVAL (operands[3]) + INTVAL (operands[4]) + >= GET_MODE_PRECISION (<MODE>mode)" + [(set (match_dup 5) + (lshiftrt:GPR (match_dup 2) + (match_dup 4))) + (set (match_dup 0) + (ior:GPR (and:GPR (match_dup 5) + (match_dup 6)) + (ashift:GPR (match_dup 1) + (match_dup 3))))] +{ + unsigned HOST_WIDE_INT mask = 1; + mask = (mask << INTVAL (operands[3])) - 1; + operands[5] = gen_reg_rtx (<MODE>mode); + operands[6] = GEN_INT (mask); +}) + +(define_split + [(set (match_operand:GPR 0 "gpc_reg_operand") + (ior:GPR (lshiftrt:GPR (match_operand:GPR 2 "gpc_reg_operand") + (match_operand:SI 4 "const_int_operand")) + (ashift:GPR (match_operand:GPR 1 "gpc_reg_operand") + (match_operand:SI 3 "const_int_operand"))))] + "can_create_pseudo_p () + && INTVAL (operands[3]) + INTVAL (operands[4]) + >= GET_MODE_PRECISION (<MODE>mode)" + [(set (match_dup 5) + (lshiftrt:GPR (match_dup 2) + (match_dup 4))) + (set (match_dup 0) + (ior:GPR (and:GPR (match_dup 5) + (match_dup 6)) + (ashift:GPR (match_dup 1) + (match_dup 3))))] +{ + unsigned HOST_WIDE_INT mask = 1; + mask = (mask << INTVAL (operands[3])) - 1; + operands[5] = gen_reg_rtx (<MODE>mode); + operands[6] = GEN_INT (mask); +}) + + +; Another important case is setting some bits to 1; we can do that with +; an insert instruction, in many cases. +(define_insn_and_split "*ior<mode>_mask" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (ior:GPR (match_operand:GPR 1 "gpc_reg_operand" "0") + (match_operand:GPR 2 "const_int_operand" "n"))) + (clobber (match_scratch:GPR 3 "=r"))] + "!logical_const_operand (operands[2], <MODE>mode) + && rs6000_is_valid_mask (operands[2], NULL, NULL, <MODE>mode)" + "#" + "&& 1" + [(set (match_dup 3) + (const_int -1)) + (set (match_dup 0) + (ior:GPR (and:GPR (rotate:GPR (match_dup 3) + (match_dup 4)) + (match_dup 2)) + (and:GPR (match_dup 1) + (match_dup 5))))] +{ + int nb, ne; + rs6000_is_valid_mask (operands[2], &nb, &ne, <MODE>mode); + if (GET_CODE (operands[3]) == SCRATCH) + operands[3] = gen_reg_rtx (<MODE>mode); + operands[4] = GEN_INT (ne); + operands[5] = GEN_INT (~UINTVAL (operands[2])); +} + [(set_attr "type" "two") + (set_attr "length" "8")]) + + +;; Now the simple shifts. + +(define_insn "rotl<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (rotate:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "reg_or_cint_operand" "rn")))] + "" + "rotl<wd>%I2 %0,%1,%<hH>2" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes")]) + +(define_insn "*rotlsi3_64" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (zero_extend:DI + (rotate:SI (match_operand:SI 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "reg_or_cint_operand" "rn"))))] + "TARGET_POWERPC64" + "rotlw%I2 %0,%1,%h2" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes")]) + +(define_insn_and_split "*rotl<mode>3_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (rotate:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:SI 2 "reg_or_cint_operand" "rn,rn")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + rotl<wd>%I2. %0,%1,%<hH>2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (rotate:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*rotl<mode>3_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (rotate:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:SI 2 "reg_or_cint_operand" "rn,rn")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (rotate:GPR (match_dup 1) + (match_dup 2)))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + rotl<wd>%I2. %0,%1,%<hH>2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (rotate:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_insn "ashl<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (ashift:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "reg_or_cint_operand" "rn")))] + "" + "sl<wd>%I2 %0,%1,%<hH>2" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes")]) + +(define_insn "*ashlsi3_64" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (zero_extend:DI + (ashift:SI (match_operand:SI 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "reg_or_cint_operand" "rn"))))] + "TARGET_POWERPC64" + "slw%I2 %0,%1,%h2" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes")]) + +(define_insn_and_split "*ashl<mode>3_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (ashift:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:SI 2 "reg_or_cint_operand" "rn,rn")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + sl<wd>%I2. %0,%1,%<hH>2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (ashift:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*ashl<mode>3_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (ashift:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:SI 2 "reg_or_cint_operand" "rn,rn")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (ashift:GPR (match_dup 1) + (match_dup 2)))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + sl<wd>%I2. %0,%1,%<hH>2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (ashift:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +;; Pretend we have a memory form of extswsli until register allocation is done +;; so that we use LWZ to load the value from memory, instead of LWA. +(define_insn_and_split "ashdi3_extswsli" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r,r") + (ashift:DI + (sign_extend:DI (match_operand:SI 1 "reg_or_mem_operand" "r,m")) + (match_operand:DI 2 "u6bit_cint_operand" "n,n")))] + "TARGET_EXTSWSLI" + "@ + extswsli %0,%1,%2 + #" + "&& reload_completed && MEM_P (operands[1])" + [(set (match_dup 3) + (match_dup 1)) + (set (match_dup 0) + (ashift:DI (sign_extend:DI (match_dup 3)) + (match_dup 2)))] +{ + operands[3] = gen_lowpart (SImode, operands[0]); +} + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "no")]) + + +(define_insn_and_split "ashdi3_extswsli_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y,?x,??y") + (compare:CC + (ashift:DI + (sign_extend:DI (match_operand:SI 1 "reg_or_mem_operand" "r,r,m,m")) + (match_operand:DI 2 "u6bit_cint_operand" "n,n,n,n")) + (const_int 0))) + (clobber (match_scratch:DI 0 "=r,r,r,r"))] + "TARGET_EXTSWSLI" + "@ + extswsli. %0,%1,%2 + # + # + #" + "&& reload_completed + && (cc_reg_not_cr0_operand (operands[3], CCmode) + || memory_operand (operands[1], SImode))" + [(pc)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx shift = operands[2]; + rtx cr = operands[3]; + rtx src2; + + if (!MEM_P (src)) + src2 = src; + else + { + src2 = gen_lowpart (SImode, dest); + emit_move_insn (src2, src); + } + + if (REGNO (cr) == CR0_REGNO) + { + emit_insn (gen_ashdi3_extswsli_dot2 (dest, src2, shift, cr)); + DONE; + } + + emit_insn (gen_ashdi3_extswsli (dest, src2, shift)); + emit_insn (gen_rtx_SET (cr, gen_rtx_COMPARE (CCmode, dest, const0_rtx))); + DONE; +} + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "no") + (set_attr "dot" "yes") + (set_attr "length" "4,8,8,12")]) + +(define_insn_and_split "ashdi3_extswsli_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y,?x,??y") + (compare:CC + (ashift:DI + (sign_extend:DI (match_operand:SI 1 "reg_or_mem_operand" "r,r,m,m")) + (match_operand:DI 2 "u6bit_cint_operand" "n,n,n,n")) + (const_int 0))) + (set (match_operand:DI 0 "gpc_reg_operand" "=r,r,r,r") + (ashift:DI (sign_extend:DI (match_dup 1)) + (match_dup 2)))] + "TARGET_EXTSWSLI" + "@ + extswsli. %0,%1,%2 + # + # + #" + "&& reload_completed + && (cc_reg_not_cr0_operand (operands[3], CCmode) + || memory_operand (operands[1], SImode))" + [(pc)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx shift = operands[2]; + rtx cr = operands[3]; + rtx src2; + + if (!MEM_P (src)) + src2 = src; + else + { + src2 = gen_lowpart (SImode, dest); + emit_move_insn (src2, src); + } + + if (REGNO (cr) == CR0_REGNO) + { + emit_insn (gen_ashdi3_extswsli_dot2 (dest, src2, shift, cr)); + DONE; + } + + emit_insn (gen_ashdi3_extswsli (dest, src2, shift)); + emit_insn (gen_rtx_SET (cr, gen_rtx_COMPARE (CCmode, dest, const0_rtx))); + DONE; +} + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "no") + (set_attr "dot" "yes") + (set_attr "length" "4,8,8,12")]) + +(define_insn "lshr<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (lshiftrt:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "reg_or_cint_operand" "rn")))] + "" + "sr<wd>%I2 %0,%1,%<hH>2" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes")]) + +(define_insn "*lshrsi3_64" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (zero_extend:DI + (lshiftrt:SI (match_operand:SI 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "reg_or_cint_operand" "rn"))))] + "TARGET_POWERPC64" + "srw%I2 %0,%1,%h2" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes")]) + +(define_insn_and_split "*lshr<mode>3_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (lshiftrt:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:SI 2 "reg_or_cint_operand" "rn,rn")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r"))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + sr<wd>%I2. %0,%1,%<hH>2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (lshiftrt:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*lshr<mode>3_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (lshiftrt:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:SI 2 "reg_or_cint_operand" "rn,rn")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (lshiftrt:GPR (match_dup 1) + (match_dup 2)))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + sr<wd>%I2. %0,%1,%<hH>2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(set (match_dup 0) + (lshiftrt:GPR (match_dup 1) + (match_dup 2))) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + + +(define_insn "ashr<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (ashiftrt:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "reg_or_cint_operand" "rn"))) + (clobber (reg:GPR CA_REGNO))] + "" + "sra<wd>%I2 %0,%1,%<hH>2" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes")]) + +(define_insn "*ashrsi3_64" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (sign_extend:DI + (ashiftrt:SI (match_operand:SI 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "reg_or_cint_operand" "rn")))) + (clobber (reg:SI CA_REGNO))] + "TARGET_POWERPC64" + "sraw%I2 %0,%1,%h2" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes")]) + +(define_insn_and_split "*ashr<mode>3_dot" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (ashiftrt:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:SI 2 "reg_or_cint_operand" "rn,rn")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=r,r")) + (clobber (reg:GPR CA_REGNO))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + sra<wd>%I2. %0,%1,%<hH>2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(parallel [(set (match_dup 0) + (ashiftrt:GPR (match_dup 1) + (match_dup 2))) + (clobber (reg:GPR CA_REGNO))]) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +(define_insn_and_split "*ashr<mode>3_dot2" + [(set (match_operand:CC 3 "cc_reg_operand" "=x,?y") + (compare:CC (ashiftrt:GPR (match_operand:GPR 1 "gpc_reg_operand" "r,r") + (match_operand:SI 2 "reg_or_cint_operand" "rn,rn")) + (const_int 0))) + (set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (ashiftrt:GPR (match_dup 1) + (match_dup 2))) + (clobber (reg:GPR CA_REGNO))] + "<MODE>mode == Pmode && rs6000_gen_cell_microcode" + "@ + sra<wd>%I2. %0,%1,%<hH>2 + #" + "&& reload_completed && cc_reg_not_cr0_operand (operands[3], CCmode)" + [(parallel [(set (match_dup 0) + (ashiftrt:GPR (match_dup 1) + (match_dup 2))) + (clobber (reg:GPR CA_REGNO))]) + (set (match_dup 3) + (compare:CC (match_dup 0) + (const_int 0)))] + "" + [(set_attr "type" "shift") + (set_attr "maybe_var_shift" "yes") + (set_attr "dot" "yes") + (set_attr "length" "4,8")]) + +;; Builtins to replace a division to generate FRE reciprocal estimate +;; instructions and the necessary fixup instructions +(define_expand "recip<mode>3" + [(match_operand:RECIPF 0 "gpc_reg_operand" "") + (match_operand:RECIPF 1 "gpc_reg_operand" "") + (match_operand:RECIPF 2 "gpc_reg_operand" "")] + "RS6000_RECIP_HAVE_RE_P (<MODE>mode)" +{ + rs6000_emit_swdiv (operands[0], operands[1], operands[2], false); + DONE; +}) + +;; Split to create division from FRE/FRES/etc. and fixup instead of the normal +;; hardware division. This is only done before register allocation and with +;; -ffast-math. This must appear before the divsf3/divdf3 insns. +;; We used to also check optimize_insn_for_speed_p () but problems with guessed +;; frequencies (pr68212/pr77536) yields that unreliable so it was removed. +(define_split + [(set (match_operand:RECIPF 0 "gpc_reg_operand" "") + (div:RECIPF (match_operand 1 "gpc_reg_operand" "") + (match_operand 2 "gpc_reg_operand" "")))] + "RS6000_RECIP_AUTO_RE_P (<MODE>mode) + && can_create_pseudo_p () && flag_finite_math_only + && !flag_trapping_math && flag_reciprocal_math" + [(const_int 0)] +{ + rs6000_emit_swdiv (operands[0], operands[1], operands[2], true); + DONE; +}) + +;; Builtins to replace 1/sqrt(x) with instructions using RSQRTE and the +;; appropriate fixup. +(define_expand "rsqrt<mode>2" + [(match_operand:RECIPF 0 "gpc_reg_operand" "") + (match_operand:RECIPF 1 "gpc_reg_operand" "")] + "RS6000_RECIP_HAVE_RSQRTE_P (<MODE>mode)" +{ + rs6000_emit_swsqrt (operands[0], operands[1], 1); + DONE; +}) + +;; Floating-point insns, excluding normal data motion. We combine the SF/DF +;; modes here, and also add in conditional vsx/power8-vector support to access +;; values in the traditional Altivec registers if the appropriate +;; -mupper-regs-{df,sf} option is enabled. + +(define_expand "abs<mode>2" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "") + (abs:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "")))] + "TARGET_<MODE>_INSN" + "") + +(define_insn "*abs<mode>2_fpr" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv>") + (abs:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv>")))] + "TARGET_<MODE>_FPR" + "@ + fabs %0,%1 + xsabsdp %x0,%x1" + [(set_attr "type" "fpsimple") + (set_attr "fp_type" "fp_addsub_<Fs>")]) + +(define_insn "*nabs<mode>2_fpr" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv>") + (neg:SFDF + (abs:SFDF + (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv>"))))] + "TARGET_<MODE>_FPR" + "@ + fnabs %0,%1 + xsnabsdp %x0,%x1" + [(set_attr "type" "fpsimple") + (set_attr "fp_type" "fp_addsub_<Fs>")]) + +(define_expand "neg<mode>2" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "") + (neg:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "")))] + "TARGET_<MODE>_INSN" + "") + +(define_insn "*neg<mode>2_fpr" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv>") + (neg:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv>")))] + "TARGET_<MODE>_FPR" + "@ + fneg %0,%1 + xsnegdp %x0,%x1" + [(set_attr "type" "fpsimple") + (set_attr "fp_type" "fp_addsub_<Fs>")]) + +(define_expand "add<mode>3" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "") + (plus:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "") + (match_operand:SFDF 2 "gpc_reg_operand" "")))] + "TARGET_<MODE>_INSN" + "") + +(define_insn "*add<mode>3_fpr" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv2>") + (plus:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "%<Ff>,<Fv2>") + (match_operand:SFDF 2 "gpc_reg_operand" "<Ff>,<Fv2>")))] + "TARGET_<MODE>_FPR" + "@ + fadd<Ftrad> %0,%1,%2 + xsadd<Fvsx> %x0,%x1,%x2" + [(set_attr "type" "fp") + (set_attr "fp_type" "fp_addsub_<Fs>")]) + +(define_expand "sub<mode>3" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "") + (minus:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "") + (match_operand:SFDF 2 "gpc_reg_operand" "")))] + "TARGET_<MODE>_INSN" + "") + +(define_insn "*sub<mode>3_fpr" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv2>") + (minus:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv2>") + (match_operand:SFDF 2 "gpc_reg_operand" "<Ff>,<Fv2>")))] + "TARGET_<MODE>_FPR" + "@ + fsub<Ftrad> %0,%1,%2 + xssub<Fvsx> %x0,%x1,%x2" + [(set_attr "type" "fp") + (set_attr "fp_type" "fp_addsub_<Fs>")]) + +(define_expand "mul<mode>3" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "") + (mult:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "") + (match_operand:SFDF 2 "gpc_reg_operand" "")))] + "TARGET_<MODE>_INSN" + "") + +(define_insn "*mul<mode>3_fpr" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv2>") + (mult:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "%<Ff>,<Fv2>") + (match_operand:SFDF 2 "gpc_reg_operand" "<Ff>,<Fv2>")))] + "TARGET_<MODE>_FPR" + "@ + fmul<Ftrad> %0,%1,%2 + xsmul<Fvsx> %x0,%x1,%x2" + [(set_attr "type" "dmul") + (set_attr "fp_type" "fp_mul_<Fs>")]) + +(define_expand "div<mode>3" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "") + (div:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "") + (match_operand:SFDF 2 "gpc_reg_operand" "")))] + "TARGET_<MODE>_INSN && !TARGET_SIMPLE_FPU" +{ + if (RS6000_RECIP_AUTO_RE_P (<MODE>mode) + && can_create_pseudo_p () && flag_finite_math_only + && !flag_trapping_math && flag_reciprocal_math) + { + rs6000_emit_swdiv (operands[0], operands[1], operands[2], true); + DONE; + } +}) + +(define_insn "*div<mode>3_fpr" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv2>") + (div:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv2>") + (match_operand:SFDF 2 "gpc_reg_operand" "<Ff>,<Fv2>")))] + "TARGET_<MODE>_FPR && !TARGET_SIMPLE_FPU" + "@ + fdiv<Ftrad> %0,%1,%2 + xsdiv<Fvsx> %x0,%x1,%x2" + [(set_attr "type" "<Fs>div") + (set_attr "fp_type" "fp_div_<Fs>")]) + +(define_insn "*sqrt<mode>2_internal" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv2>") + (sqrt:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv2>")))] + "TARGET_<MODE>_FPR && !TARGET_SIMPLE_FPU + && (TARGET_PPC_GPOPT || (<MODE>mode == SFmode && TARGET_XILINX_FPU))" + "@ + fsqrt<Ftrad> %0,%1 + xssqrt<Fvsx> %x0,%x1" + [(set_attr "type" "<Fs>sqrt") + (set_attr "fp_type" "fp_sqrt_<Fs>")]) + +(define_expand "sqrt<mode>2" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "") + (sqrt:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "")))] + "TARGET_<MODE>_FPR && !TARGET_SIMPLE_FPU + && (TARGET_PPC_GPOPT || (<MODE>mode == SFmode && TARGET_XILINX_FPU))" +{ + if (<MODE>mode == SFmode + && TARGET_RECIP_PRECISION + && RS6000_RECIP_HAVE_RSQRTE_P (<MODE>mode) + && !optimize_function_for_size_p (cfun) + && flag_finite_math_only && !flag_trapping_math + && flag_unsafe_math_optimizations) + { + rs6000_emit_swsqrt (operands[0], operands[1], 0); + DONE; + } +}) + +;; Floating point reciprocal approximation +(define_insn "fre<Fs>" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv2>") + (unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv2>")] + UNSPEC_FRES))] + "TARGET_<FFRE>" + "@ + fre<Ftrad> %0,%1 + xsre<Fvsx> %x0,%x1" + [(set_attr "type" "fp")]) + +(define_insn "*rsqrt<mode>2" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv2>") + (unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv2>")] + UNSPEC_RSQRT))] + "RS6000_RECIP_HAVE_RSQRTE_P (<MODE>mode)" + "@ + frsqrte<Ftrad> %0,%1 + xsrsqrte<Fvsx> %x0,%x1" + [(set_attr "type" "fp")]) + +;; Floating point comparisons +(define_insn "*cmp<mode>_fpr" + [(set (match_operand:CCFP 0 "cc_reg_operand" "=y,y") + (compare:CCFP (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv2>") + (match_operand:SFDF 2 "gpc_reg_operand" "<Ff>,<Fv2>")))] + "TARGET_<MODE>_FPR" + "@ + fcmpu %0,%1,%2 + xscmpudp %0,%x1,%x2" + [(set_attr "type" "fpcompare")]) + +;; Floating point conversions +(define_expand "extendsfdf2" + [(set (match_operand:DF 0 "gpc_reg_operand") + (float_extend:DF (match_operand:SF 1 "reg_or_none500mem_operand")))] + "TARGET_HARD_FLOAT && ((TARGET_FPRS && TARGET_DOUBLE_FLOAT) || TARGET_E500_DOUBLE)" +{ + if (HONOR_SNANS (SFmode)) + operands[1] = force_reg (SFmode, operands[1]); +}) + +(define_insn_and_split "*extendsfdf2_fpr" + [(set (match_operand:DF 0 "gpc_reg_operand" "=d,?d,d,ws,?ws,wu,wb") + (float_extend:DF (match_operand:SF 1 "reg_or_mem_operand" "0,f,m,0,wy,Z,wY")))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && !HONOR_SNANS (SFmode)" + "@ + # + fmr %0,%1 + lfs%U1%X1 %0,%1 + # + xscpsgndp %x0,%x1,%x1 + lxsspx %x0,%y1 + lxssp %0,%1" + "&& reload_completed && REG_P (operands[1]) && REGNO (operands[0]) == REGNO (operands[1])" + [(const_int 0)] +{ + emit_note (NOTE_INSN_DELETED); + DONE; +} + [(set_attr "type" "fp,fpsimple,fpload,fp,fpsimple,fpload,fpload")]) + +(define_insn "*extendsfdf2_snan" + [(set (match_operand:DF 0 "gpc_reg_operand" "=d,ws") + (float_extend:DF (match_operand:SF 1 "gpc_reg_operand" "f,wy")))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && HONOR_SNANS (SFmode)" + "@ + frsp %0,%1 + xsrsp %x0,%x1" + [(set_attr "type" "fp")]) + +(define_expand "truncdfsf2" + [(set (match_operand:SF 0 "gpc_reg_operand" "") + (float_truncate:SF (match_operand:DF 1 "gpc_reg_operand" "")))] + "TARGET_HARD_FLOAT && ((TARGET_FPRS && TARGET_DOUBLE_FLOAT) || TARGET_E500_DOUBLE)" + "") + +(define_insn "*truncdfsf2_fpr" + [(set (match_operand:SF 0 "gpc_reg_operand" "=f,wy") + (float_truncate:SF (match_operand:DF 1 "gpc_reg_operand" "d,ws")))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT" + "@ + frsp %0,%1 + xsrsp %x0,%x1" + [(set_attr "type" "fp")]) + +;; This expander is here to avoid FLOAT_WORDS_BIGENDIAN tests in +;; builtins.c and optabs.c that are not correct for IBM long double +;; when little-endian. +(define_expand "signbit<mode>2" + [(set (match_dup 2) + (float_truncate:DF (match_operand:FLOAT128 1 "gpc_reg_operand" ""))) + (set (match_dup 3) + (subreg:DI (match_dup 2) 0)) + (set (match_dup 4) + (match_dup 5)) + (set (match_operand:SI 0 "gpc_reg_operand" "") + (match_dup 6))] + "TARGET_HARD_FLOAT + && (TARGET_FPRS || TARGET_E500_DOUBLE) + && (!FLOAT128_IEEE_P (<MODE>mode) + || (TARGET_POWERPC64 && TARGET_DIRECT_MOVE))" +{ + if (FLOAT128_IEEE_P (<MODE>mode)) + { + if (<MODE>mode == KFmode) + emit_insn (gen_signbitkf2_dm (operands[0], operands[1])); + else if (<MODE>mode == TFmode) + emit_insn (gen_signbittf2_dm (operands[0], operands[1])); + else + gcc_unreachable (); + DONE; + } + operands[2] = gen_reg_rtx (DFmode); + operands[3] = gen_reg_rtx (DImode); + if (TARGET_POWERPC64) + { + operands[4] = gen_reg_rtx (DImode); + operands[5] = gen_rtx_LSHIFTRT (DImode, operands[3], GEN_INT (63)); + operands[6] = gen_rtx_SUBREG (SImode, operands[4], + WORDS_BIG_ENDIAN ? 4 : 0); + } + else + { + operands[4] = gen_reg_rtx (SImode); + operands[5] = gen_rtx_SUBREG (SImode, operands[3], + WORDS_BIG_ENDIAN ? 0 : 4); + operands[6] = gen_rtx_LSHIFTRT (SImode, operands[4], GEN_INT (31)); + } +}) + +(define_expand "copysign<mode>3" + [(set (match_dup 3) + (abs:SFDF (match_operand:SFDF 1 "gpc_reg_operand" ""))) + (set (match_dup 4) + (neg:SFDF (abs:SFDF (match_dup 1)))) + (set (match_operand:SFDF 0 "gpc_reg_operand" "") + (if_then_else:SFDF (ge (match_operand:SFDF 2 "gpc_reg_operand" "") + (match_dup 5)) + (match_dup 3) + (match_dup 4)))] + "TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT> + && ((TARGET_PPC_GFXOPT + && !HONOR_NANS (<MODE>mode) + && !HONOR_SIGNED_ZEROS (<MODE>mode)) + || TARGET_CMPB + || VECTOR_UNIT_VSX_P (<MODE>mode))" +{ + if (TARGET_CMPB || VECTOR_UNIT_VSX_P (<MODE>mode)) + { + emit_insn (gen_copysign<mode>3_fcpsgn (operands[0], operands[1], + operands[2])); + DONE; + } + + operands[3] = gen_reg_rtx (<MODE>mode); + operands[4] = gen_reg_rtx (<MODE>mode); + operands[5] = CONST0_RTX (<MODE>mode); + }) + +;; Optimize signbit on 64-bit systems with direct move to avoid doing the store +;; and load. +(define_insn_and_split "signbit<mode>2_dm" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r,r,r") + (unspec:SI + [(match_operand:SIGNBIT 1 "input_operand" "wa,m,r")] + UNSPEC_SIGNBIT))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rs6000_split_signbit (operands[0], operands[1]); + DONE; +} + [(set_attr "length" "8,8,4") + (set_attr "type" "mftgpr,load,integer")]) + +(define_insn_and_split "*signbit<mode>2_dm_<su>ext" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r,r,r") + (any_extend:DI + (unspec:SI + [(match_operand:SIGNBIT 1 "input_operand" "wa,m,r")] + UNSPEC_SIGNBIT)))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rs6000_split_signbit (operands[0], operands[1]); + DONE; +} + [(set_attr "length" "8,8,4") + (set_attr "type" "mftgpr,load,integer")]) + +;; MODES_TIEABLE_P doesn't allow DImode to be tied with the various floating +;; point types, which makes normal SUBREG's problematical. Instead use a +;; special pattern to avoid using a normal movdi. +(define_insn "signbit<mode>2_dm2" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (unspec:DI [(match_operand:SIGNBIT 1 "gpc_reg_operand" "wa") + (const_int 0)] + UNSPEC_SIGNBIT))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "mfvsrd %0,%x1" + [(set_attr "type" "mftgpr")]) + + +;; Use an unspec rather providing an if-then-else in RTL, to prevent the +;; compiler from optimizing -0.0 +(define_insn "copysign<mode>3_fcpsgn" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv>") + (unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv>") + (match_operand:SFDF 2 "gpc_reg_operand" "<Ff>,<Fv>")] + UNSPEC_COPYSIGN))] + "TARGET_<MODE>_FPR && (TARGET_CMPB || VECTOR_UNIT_VSX_P (<MODE>mode))" + "@ + fcpsgn %0,%2,%1 + xscpsgndp %x0,%x2,%x1" + [(set_attr "type" "fpsimple")]) + +;; For MIN, MAX, and conditional move, we use DEFINE_EXPAND's that involve a +;; fsel instruction and some auxiliary computations. Then we just have a +;; single DEFINE_INSN for fsel and the define_splits to make them if made by +;; combine. +;; For MIN, MAX on non-VSX machines, and conditional move all of the time, we +;; use DEFINE_EXPAND's that involve a fsel instruction and some auxiliary +;; computations. Then we just have a single DEFINE_INSN for fsel and the +;; define_splits to make them if made by combine. On VSX machines we have the +;; min/max instructions. +;; +;; On VSX, we only check for TARGET_VSX instead of checking for a vsx/p8 vector +;; to allow either DF/SF to use only traditional registers. + +(define_expand "s<minmax><mode>3" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "") + (fp_minmax:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "") + (match_operand:SFDF 2 "gpc_reg_operand" "")))] + "TARGET_MINMAX_<MODE>" +{ + rs6000_emit_minmax (operands[0], <SMINMAX>, operands[1], operands[2]); + DONE; +}) + +(define_insn "*s<minmax><mode>3_vsx" + [(set (match_operand:SFDF 0 "vsx_register_operand" "=<Fv>") + (fp_minmax:SFDF (match_operand:SFDF 1 "vsx_register_operand" "<Fv>") + (match_operand:SFDF 2 "vsx_register_operand" "<Fv>")))] + "TARGET_VSX && TARGET_<MODE>_FPR" +{ + return (TARGET_P9_MINMAX + ? "xs<minmax>cdp %x0,%x1,%x2" + : "xs<minmax>dp %x0,%x1,%x2"); +} + [(set_attr "type" "fp")]) + +;; The conditional move instructions allow us to perform max and min operations +;; even when we don't have the appropriate max/min instruction using the FSEL +;; instruction. + +(define_insn_and_split "*s<minmax><mode>3_fpr" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "") + (fp_minmax:SFDF (match_operand:SFDF 1 "gpc_reg_operand" "") + (match_operand:SFDF 2 "gpc_reg_operand" "")))] + "!TARGET_VSX && TARGET_MINMAX_<MODE>" + "#" + "&& 1" + [(const_int 0)] +{ + rs6000_emit_minmax (operands[0], <SMINMAX>, operands[1], operands[2]); + DONE; +}) + +(define_expand "mov<mode>cc" + [(set (match_operand:GPR 0 "gpc_reg_operand" "") + (if_then_else:GPR (match_operand 1 "comparison_operator" "") + (match_operand:GPR 2 "gpc_reg_operand" "") + (match_operand:GPR 3 "gpc_reg_operand" "")))] + "TARGET_ISEL<sel>" + " +{ + if (rs6000_emit_cmove (operands[0], operands[1], operands[2], operands[3])) + DONE; + else + FAIL; +}") + +;; We use the BASE_REGS for the isel input operands because, if rA is +;; 0, the value of 0 is placed in rD upon truth. Similarly for rB +;; because we may switch the operands and rB may end up being rA. +;; +;; We need 2 patterns: an unsigned and a signed pattern. We could +;; leave out the mode in operand 4 and use one pattern, but reload can +;; change the mode underneath our feet and then gets confused trying +;; to reload the value. +(define_insn "isel_signed_<mode>" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (if_then_else:GPR + (match_operator 1 "scc_comparison_operator" + [(match_operand:CC 4 "cc_reg_operand" "y,y") + (const_int 0)]) + (match_operand:GPR 2 "reg_or_cint_operand" "O,b") + (match_operand:GPR 3 "gpc_reg_operand" "r,r")))] + "TARGET_ISEL<sel>" + "* +{ return output_isel (operands); }" + [(set_attr "type" "isel") + (set_attr "length" "4")]) + +(define_insn "isel_unsigned_<mode>" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r,r") + (if_then_else:GPR + (match_operator 1 "scc_comparison_operator" + [(match_operand:CCUNS 4 "cc_reg_operand" "y,y") + (const_int 0)]) + (match_operand:GPR 2 "reg_or_cint_operand" "O,b") + (match_operand:GPR 3 "gpc_reg_operand" "r,r")))] + "TARGET_ISEL<sel>" + "* +{ return output_isel (operands); }" + [(set_attr "type" "isel") + (set_attr "length" "4")]) + +;; These patterns can be useful for combine; they let combine know that +;; isel can handle reversed comparisons so long as the operands are +;; registers. + +(define_insn "*isel_reversed_signed_<mode>" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (if_then_else:GPR + (match_operator 1 "scc_rev_comparison_operator" + [(match_operand:CC 4 "cc_reg_operand" "y") + (const_int 0)]) + (match_operand:GPR 2 "gpc_reg_operand" "b") + (match_operand:GPR 3 "gpc_reg_operand" "b")))] + "TARGET_ISEL<sel>" + "* +{ return output_isel (operands); }" + [(set_attr "type" "isel") + (set_attr "length" "4")]) + +(define_insn "*isel_reversed_unsigned_<mode>" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (if_then_else:GPR + (match_operator 1 "scc_rev_comparison_operator" + [(match_operand:CCUNS 4 "cc_reg_operand" "y") + (const_int 0)]) + (match_operand:GPR 2 "gpc_reg_operand" "b") + (match_operand:GPR 3 "gpc_reg_operand" "b")))] + "TARGET_ISEL<sel>" + "* +{ return output_isel (operands); }" + [(set_attr "type" "isel") + (set_attr "length" "4")]) + +;; Floating point conditional move +(define_expand "mov<mode>cc" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "") + (if_then_else:SFDF (match_operand 1 "comparison_operator" "") + (match_operand:SFDF 2 "gpc_reg_operand" "") + (match_operand:SFDF 3 "gpc_reg_operand" "")))] + "TARGET_<MODE>_FPR && TARGET_PPC_GFXOPT" + " +{ + if (rs6000_emit_cmove (operands[0], operands[1], operands[2], operands[3])) + DONE; + else + FAIL; +}") + +(define_insn "*fsel<SFDF:mode><SFDF2:mode>4" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=&<SFDF:rreg2>") + (if_then_else:SFDF + (ge (match_operand:SFDF2 1 "gpc_reg_operand" "<SFDF2:rreg2>") + (match_operand:SFDF2 4 "zero_fp_constant" "F")) + (match_operand:SFDF 2 "gpc_reg_operand" "<SFDF:rreg2>") + (match_operand:SFDF 3 "gpc_reg_operand" "<SFDF:rreg2>")))] + "TARGET_<MODE>_FPR && TARGET_PPC_GFXOPT" + "fsel %0,%1,%2,%3" + [(set_attr "type" "fp")]) + +(define_insn_and_split "*mov<SFDF:mode><SFDF2:mode>cc_p9" + [(set (match_operand:SFDF 0 "vsx_register_operand" "=&<SFDF:Fv>,<SFDF:Fv>") + (if_then_else:SFDF + (match_operator:CCFP 1 "fpmask_comparison_operator" + [(match_operand:SFDF2 2 "vsx_register_operand" "<SFDF2:Fv>,<SFDF2:Fv>") + (match_operand:SFDF2 3 "vsx_register_operand" "<SFDF2:Fv>,<SFDF2:Fv>")]) + (match_operand:SFDF 4 "vsx_register_operand" "<SFDF:Fv>,<SFDF:Fv>") + (match_operand:SFDF 5 "vsx_register_operand" "<SFDF:Fv>,<SFDF:Fv>"))) + (clobber (match_scratch:V2DI 6 "=0,&wa"))] + "TARGET_P9_MINMAX" + "#" + "" + [(set (match_dup 6) + (if_then_else:V2DI (match_dup 1) + (match_dup 7) + (match_dup 8))) + (set (match_dup 0) + (if_then_else:SFDF (ne (match_dup 6) + (match_dup 8)) + (match_dup 4) + (match_dup 5)))] +{ + if (GET_CODE (operands[6]) == SCRATCH) + operands[6] = gen_reg_rtx (V2DImode); + + operands[7] = CONSTM1_RTX (V2DImode); + operands[8] = CONST0_RTX (V2DImode); +} + [(set_attr "length" "8") + (set_attr "type" "vecperm")]) + +;; Handle inverting the fpmask comparisons. +(define_insn_and_split "*mov<SFDF:mode><SFDF2:mode>cc_invert_p9" + [(set (match_operand:SFDF 0 "vsx_register_operand" "=&<SFDF:Fv>,<SFDF:Fv>") + (if_then_else:SFDF + (match_operator:CCFP 1 "invert_fpmask_comparison_operator" + [(match_operand:SFDF2 2 "vsx_register_operand" "<SFDF2:Fv>,<SFDF2:Fv>") + (match_operand:SFDF2 3 "vsx_register_operand" "<SFDF2:Fv>,<SFDF2:Fv>")]) + (match_operand:SFDF 4 "vsx_register_operand" "<SFDF:Fv>,<SFDF:Fv>") + (match_operand:SFDF 5 "vsx_register_operand" "<SFDF:Fv>,<SFDF:Fv>"))) + (clobber (match_scratch:V2DI 6 "=0,&wa"))] + "TARGET_P9_MINMAX" + "#" + "&& 1" + [(set (match_dup 6) + (if_then_else:V2DI (match_dup 9) + (match_dup 7) + (match_dup 8))) + (set (match_dup 0) + (if_then_else:SFDF (ne (match_dup 6) + (match_dup 8)) + (match_dup 5) + (match_dup 4)))] +{ + rtx op1 = operands[1]; + enum rtx_code cond = reverse_condition_maybe_unordered (GET_CODE (op1)); + + if (GET_CODE (operands[6]) == SCRATCH) + operands[6] = gen_reg_rtx (V2DImode); + + operands[7] = CONSTM1_RTX (V2DImode); + operands[8] = CONST0_RTX (V2DImode); + + operands[9] = gen_rtx_fmt_ee (cond, CCFPmode, operands[2], operands[3]); +} + [(set_attr "length" "8") + (set_attr "type" "vecperm")]) + +(define_insn "*fpmask<mode>" + [(set (match_operand:V2DI 0 "vsx_register_operand" "=wa") + (if_then_else:V2DI + (match_operator:CCFP 1 "fpmask_comparison_operator" + [(match_operand:SFDF 2 "vsx_register_operand" "<Fv>") + (match_operand:SFDF 3 "vsx_register_operand" "<Fv>")]) + (match_operand:V2DI 4 "all_ones_constant" "") + (match_operand:V2DI 5 "zero_constant" "")))] + "TARGET_P9_MINMAX" + "xscmp%V1dp %x0,%x2,%x3" + [(set_attr "type" "fpcompare")]) + +(define_insn "*xxsel<mode>" + [(set (match_operand:SFDF 0 "vsx_register_operand" "=<Fv>") + (if_then_else:SFDF (ne (match_operand:V2DI 1 "vsx_register_operand" "wa") + (match_operand:V2DI 2 "zero_constant" "")) + (match_operand:SFDF 3 "vsx_register_operand" "<Fv>") + (match_operand:SFDF 4 "vsx_register_operand" "<Fv>")))] + "TARGET_P9_MINMAX" + "xxsel %x0,%x4,%x3,%x1" + [(set_attr "type" "vecmove")]) + + +;; Conversions to and from floating-point. + +; We don't define lfiwax/lfiwzx with the normal definition, because we +; don't want to support putting SImode in FPR registers. +(define_insn "lfiwax" + [(set (match_operand:DI 0 "gpc_reg_operand" "=d,wj,wj,wK") + (unspec:DI [(match_operand:SI 1 "reg_or_indexed_operand" "Z,Z,r,wK")] + UNSPEC_LFIWAX))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LFIWAX" + "@ + lfiwax %0,%y1 + lxsiwax %x0,%y1 + mtvsrwa %x0,%1 + vextsw2d %0,%1" + [(set_attr "type" "fpload,fpload,mffgpr,vecexts")]) + +; This split must be run before register allocation because it allocates the +; memory slot that is needed to move values to/from the FPR. We don't allocate +; it earlier to allow for the combiner to merge insns together where it might +; not be needed and also in case the insns are deleted as dead code. + +(define_insn_and_split "floatsi<mode>2_lfiwax" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Fv>") + (float:SFDF (match_operand:SI 1 "nonimmediate_operand" "r"))) + (clobber (match_scratch:DI 2 "=wi"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LFIWAX + && <SI_CONVERT_FP> && can_create_pseudo_p ()" + "#" + "" + [(pc)] + " +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx tmp; + + if (!MEM_P (src) && TARGET_POWERPC64 + && (TARGET_MFPGPR || TARGET_DIRECT_MOVE)) + tmp = convert_to_mode (DImode, src, false); + else + { + tmp = operands[2]; + if (GET_CODE (tmp) == SCRATCH) + tmp = gen_reg_rtx (DImode); + if (MEM_P (src)) + { + src = rs6000_address_for_fpconvert (src); + emit_insn (gen_lfiwax (tmp, src)); + } + else + { + rtx stack = rs6000_allocate_stack_temp (SImode, false, true); + emit_move_insn (stack, src); + emit_insn (gen_lfiwax (tmp, stack)); + } + } + emit_insn (gen_floatdi<mode>2 (dest, tmp)); + DONE; +}" + [(set_attr "length" "12") + (set_attr "type" "fpload")]) + +(define_insn_and_split "floatsi<mode>2_lfiwax_mem" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Fv>") + (float:SFDF + (sign_extend:DI + (match_operand:SI 1 "indexed_or_indirect_operand" "Z")))) + (clobber (match_scratch:DI 2 "=wi"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LFIWAX + && <SI_CONVERT_FP>" + "#" + "" + [(pc)] + " +{ + operands[1] = rs6000_address_for_fpconvert (operands[1]); + if (GET_CODE (operands[2]) == SCRATCH) + operands[2] = gen_reg_rtx (DImode); + if (TARGET_VSX_SMALL_INTEGER) + emit_insn (gen_extendsidi2 (operands[2], operands[1])); + else + emit_insn (gen_lfiwax (operands[2], operands[1])); + emit_insn (gen_floatdi<mode>2 (operands[0], operands[2])); + DONE; +}" + [(set_attr "length" "8") + (set_attr "type" "fpload")]) + +(define_insn "lfiwzx" + [(set (match_operand:DI 0 "gpc_reg_operand" "=d,wj,wj,wJwK") + (unspec:DI [(match_operand:SI 1 "reg_or_indexed_operand" "Z,Z,r,wJwK")] + UNSPEC_LFIWZX))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LFIWZX" + "@ + lfiwzx %0,%y1 + lxsiwzx %x0,%y1 + mtvsrwz %x0,%1 + xxextractuw %x0,%x1,4" + [(set_attr "type" "fpload,fpload,mftgpr,vecexts")]) + +(define_insn_and_split "floatunssi<mode>2_lfiwzx" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Fv>") + (unsigned_float:SFDF (match_operand:SI 1 "nonimmediate_operand" "r"))) + (clobber (match_scratch:DI 2 "=wi"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LFIWZX + && <SI_CONVERT_FP>" + "#" + "" + [(pc)] + " +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx tmp; + + if (!MEM_P (src) && TARGET_POWERPC64 + && (TARGET_MFPGPR || TARGET_DIRECT_MOVE)) + tmp = convert_to_mode (DImode, src, true); + else + { + tmp = operands[2]; + if (GET_CODE (tmp) == SCRATCH) + tmp = gen_reg_rtx (DImode); + if (MEM_P (src)) + { + src = rs6000_address_for_fpconvert (src); + emit_insn (gen_lfiwzx (tmp, src)); + } + else + { + rtx stack = rs6000_allocate_stack_temp (SImode, false, true); + emit_move_insn (stack, src); + emit_insn (gen_lfiwzx (tmp, stack)); + } + } + emit_insn (gen_floatdi<mode>2 (dest, tmp)); + DONE; +}" + [(set_attr "length" "12") + (set_attr "type" "fpload")]) + +(define_insn_and_split "floatunssi<mode>2_lfiwzx_mem" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Fv>") + (unsigned_float:SFDF + (zero_extend:DI + (match_operand:SI 1 "indexed_or_indirect_operand" "Z")))) + (clobber (match_scratch:DI 2 "=wi"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LFIWZX + && <SI_CONVERT_FP>" + "#" + "" + [(pc)] + " +{ + operands[1] = rs6000_address_for_fpconvert (operands[1]); + if (GET_CODE (operands[2]) == SCRATCH) + operands[2] = gen_reg_rtx (DImode); + if (TARGET_VSX_SMALL_INTEGER) + emit_insn (gen_zero_extendsidi2 (operands[2], operands[1])); + else + emit_insn (gen_lfiwzx (operands[2], operands[1])); + emit_insn (gen_floatdi<mode>2 (operands[0], operands[2])); + DONE; +}" + [(set_attr "length" "8") + (set_attr "type" "fpload")]) + +; For each of these conversions, there is a define_expand, a define_insn +; with a '#' template, and a define_split (with C code). The idea is +; to allow constant folding with the template of the define_insn, +; then to have the insns split later (between sched1 and final). + +(define_expand "floatsidf2" + [(parallel [(set (match_operand:DF 0 "gpc_reg_operand" "") + (float:DF (match_operand:SI 1 "nonimmediate_operand" ""))) + (use (match_dup 2)) + (use (match_dup 3)) + (clobber (match_dup 4)) + (clobber (match_dup 5)) + (clobber (match_dup 6))])] + "TARGET_HARD_FLOAT + && ((TARGET_FPRS && TARGET_DOUBLE_FLOAT) || TARGET_E500_DOUBLE)" + " +{ + if (TARGET_E500_DOUBLE) + { + if (!REG_P (operands[1])) + operands[1] = force_reg (SImode, operands[1]); + emit_insn (gen_spe_floatsidf2 (operands[0], operands[1])); + DONE; + } + else if (TARGET_LFIWAX && TARGET_FCFID) + { + emit_insn (gen_floatsidf2_lfiwax (operands[0], operands[1])); + DONE; + } + else if (TARGET_FCFID) + { + rtx dreg = operands[1]; + if (!REG_P (dreg)) + dreg = force_reg (SImode, dreg); + dreg = convert_to_mode (DImode, dreg, false); + emit_insn (gen_floatdidf2 (operands[0], dreg)); + DONE; + } + + if (!REG_P (operands[1])) + operands[1] = force_reg (SImode, operands[1]); + operands[2] = force_reg (SImode, GEN_INT (0x43300000)); + operands[3] = force_reg (DFmode, CONST_DOUBLE_ATOF (\"4503601774854144\", DFmode)); + operands[4] = rs6000_allocate_stack_temp (DFmode, true, false); + operands[5] = gen_reg_rtx (DFmode); + operands[6] = gen_reg_rtx (SImode); +}") + +(define_insn_and_split "*floatsidf2_internal" + [(set (match_operand:DF 0 "gpc_reg_operand" "=&d") + (float:DF (match_operand:SI 1 "gpc_reg_operand" "r"))) + (use (match_operand:SI 2 "gpc_reg_operand" "r")) + (use (match_operand:DF 3 "gpc_reg_operand" "d")) + (clobber (match_operand:DF 4 "offsettable_mem_operand" "=o")) + (clobber (match_operand:DF 5 "gpc_reg_operand" "=&d")) + (clobber (match_operand:SI 6 "gpc_reg_operand" "=&r"))] + "! TARGET_FCFID && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT" + "#" + "" + [(pc)] + " +{ + rtx lowword, highword; + gcc_assert (MEM_P (operands[4])); + highword = adjust_address (operands[4], SImode, 0); + lowword = adjust_address (operands[4], SImode, 4); + if (! WORDS_BIG_ENDIAN) + std::swap (lowword, highword); + + emit_insn (gen_xorsi3 (operands[6], operands[1], + GEN_INT (~ (HOST_WIDE_INT) 0x7fffffff))); + emit_move_insn (lowword, operands[6]); + emit_move_insn (highword, operands[2]); + emit_move_insn (operands[5], operands[4]); + emit_insn (gen_subdf3 (operands[0], operands[5], operands[3])); + DONE; +}" + [(set_attr "length" "24") + (set_attr "type" "fp")]) + +;; If we don't have a direct conversion to single precision, don't enable this +;; conversion for 32-bit without fast math, because we don't have the insn to +;; generate the fixup swizzle to avoid double rounding problems. +(define_expand "floatunssisf2" + [(set (match_operand:SF 0 "gpc_reg_operand" "") + (unsigned_float:SF (match_operand:SI 1 "nonimmediate_operand" "")))] + "TARGET_HARD_FLOAT && TARGET_SINGLE_FLOAT + && (!TARGET_FPRS + || (TARGET_FPRS + && ((TARGET_FCFIDUS && TARGET_LFIWZX) + || (TARGET_DOUBLE_FLOAT && TARGET_FCFID + && (TARGET_POWERPC64 || flag_unsafe_math_optimizations)))))" + " +{ + if (!TARGET_FPRS) + { + if (!REG_P (operands[1])) + operands[1] = force_reg (SImode, operands[1]); + } + else if (TARGET_LFIWZX && TARGET_FCFIDUS) + { + emit_insn (gen_floatunssisf2_lfiwzx (operands[0], operands[1])); + DONE; + } + else + { + rtx dreg = operands[1]; + if (!REG_P (dreg)) + dreg = force_reg (SImode, dreg); + dreg = convert_to_mode (DImode, dreg, true); + emit_insn (gen_floatdisf2 (operands[0], dreg)); + DONE; + } +}") + +(define_expand "floatunssidf2" + [(parallel [(set (match_operand:DF 0 "gpc_reg_operand" "") + (unsigned_float:DF (match_operand:SI 1 "nonimmediate_operand" ""))) + (use (match_dup 2)) + (use (match_dup 3)) + (clobber (match_dup 4)) + (clobber (match_dup 5))])] + "TARGET_HARD_FLOAT + && ((TARGET_FPRS && TARGET_DOUBLE_FLOAT) || TARGET_E500_DOUBLE)" + " +{ + if (TARGET_E500_DOUBLE) + { + if (!REG_P (operands[1])) + operands[1] = force_reg (SImode, operands[1]); + emit_insn (gen_spe_floatunssidf2 (operands[0], operands[1])); + DONE; + } + else if (TARGET_LFIWZX && TARGET_FCFID) + { + emit_insn (gen_floatunssidf2_lfiwzx (operands[0], operands[1])); + DONE; + } + else if (TARGET_FCFID) + { + rtx dreg = operands[1]; + if (!REG_P (dreg)) + dreg = force_reg (SImode, dreg); + dreg = convert_to_mode (DImode, dreg, true); + emit_insn (gen_floatdidf2 (operands[0], dreg)); + DONE; + } + + if (!REG_P (operands[1])) + operands[1] = force_reg (SImode, operands[1]); + operands[2] = force_reg (SImode, GEN_INT (0x43300000)); + operands[3] = force_reg (DFmode, CONST_DOUBLE_ATOF (\"4503599627370496\", DFmode)); + operands[4] = rs6000_allocate_stack_temp (DFmode, true, false); + operands[5] = gen_reg_rtx (DFmode); +}") + +(define_insn_and_split "*floatunssidf2_internal" + [(set (match_operand:DF 0 "gpc_reg_operand" "=&d") + (unsigned_float:DF (match_operand:SI 1 "gpc_reg_operand" "r"))) + (use (match_operand:SI 2 "gpc_reg_operand" "r")) + (use (match_operand:DF 3 "gpc_reg_operand" "d")) + (clobber (match_operand:DF 4 "offsettable_mem_operand" "=o")) + (clobber (match_operand:DF 5 "gpc_reg_operand" "=&d"))] + "! TARGET_FCFIDU && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && !(TARGET_FCFID && TARGET_POWERPC64)" + "#" + "" + [(pc)] + " +{ + rtx lowword, highword; + gcc_assert (MEM_P (operands[4])); + highword = adjust_address (operands[4], SImode, 0); + lowword = adjust_address (operands[4], SImode, 4); + if (! WORDS_BIG_ENDIAN) + std::swap (lowword, highword); + + emit_move_insn (lowword, operands[1]); + emit_move_insn (highword, operands[2]); + emit_move_insn (operands[5], operands[4]); + emit_insn (gen_subdf3 (operands[0], operands[5], operands[3])); + DONE; +}" + [(set_attr "length" "20") + (set_attr "type" "fp")]) + +;; ISA 3.0 adds instructions lxsi[bh]zx to directly load QImode and HImode to +;; vector registers. These insns favor doing the sign/zero extension in +;; the vector registers, rather then loading up a GPR, doing a sign/zero +;; extension and then a direct move. + +(define_expand "float<QHI:mode><FP_ISA3:mode>2" + [(parallel [(set (match_operand:FP_ISA3 0 "vsx_register_operand") + (float:FP_ISA3 + (match_operand:QHI 1 "input_operand"))) + (clobber (match_scratch:DI 2)) + (clobber (match_scratch:DI 3)) + (clobber (match_scratch:<QHI:MODE> 4))])] + "TARGET_P9_VECTOR && TARGET_DIRECT_MOVE && TARGET_POWERPC64 + && TARGET_VSX_SMALL_INTEGER" +{ + if (MEM_P (operands[1])) + operands[1] = rs6000_address_for_fpconvert (operands[1]); +}) + +(define_insn_and_split "*float<QHI:mode><FP_ISA3:mode>2_internal" + [(set (match_operand:FP_ISA3 0 "vsx_register_operand" "=<Fv>,<Fv>,<Fv>") + (float:FP_ISA3 + (match_operand:QHI 1 "reg_or_indexed_operand" "wK,r,Z"))) + (clobber (match_scratch:DI 2 "=wK,wi,wK")) + (clobber (match_scratch:DI 3 "=X,r,X")) + (clobber (match_scratch:<QHI:MODE> 4 "=X,X,wK"))] + "TARGET_P9_VECTOR && TARGET_DIRECT_MOVE && TARGET_POWERPC64 + && TARGET_UPPER_REGS_DI && TARGET_VSX_SMALL_INTEGER" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rtx result = operands[0]; + rtx input = operands[1]; + rtx di = operands[2]; + + if (!MEM_P (input)) + { + rtx tmp = operands[3]; + if (altivec_register_operand (input, <QHI:MODE>mode)) + emit_insn (gen_extend<QHI:mode>di2 (di, input)); + else if (GET_CODE (tmp) == SCRATCH) + emit_insn (gen_extend<QHI:mode>di2 (di, input)); + else + { + emit_insn (gen_extend<QHI:mode>di2 (tmp, input)); + emit_move_insn (di, tmp); + } + } + else + { + rtx tmp = operands[4]; + emit_move_insn (tmp, input); + emit_insn (gen_extend<QHI:mode>di2 (di, tmp)); + } + + emit_insn (gen_floatdi<FP_ISA3:mode>2 (result, di)); + DONE; +}) + +(define_expand "floatuns<QHI:mode><FP_ISA3:mode>2" + [(parallel [(set (match_operand:FP_ISA3 0 "vsx_register_operand") + (unsigned_float:FP_ISA3 + (match_operand:QHI 1 "input_operand" ""))) + (clobber (match_scratch:DI 2 "")) + (clobber (match_scratch:DI 3 ""))])] + "TARGET_P9_VECTOR && TARGET_DIRECT_MOVE && TARGET_POWERPC64 + && TARGET_VSX_SMALL_INTEGER" +{ + if (MEM_P (operands[1])) + operands[1] = rs6000_address_for_fpconvert (operands[1]); +}) + +(define_insn_and_split "*floatuns<QHI:mode><FP_ISA3:mode>2_internal" + [(set (match_operand:FP_ISA3 0 "vsx_register_operand" "=<Fv>,<Fv>,<Fv>") + (unsigned_float:FP_ISA3 + (match_operand:QHI 1 "reg_or_indexed_operand" "wK,r,Z"))) + (clobber (match_scratch:DI 2 "=wK,wi,wJwK")) + (clobber (match_scratch:DI 3 "=X,r,X"))] + "TARGET_P9_VECTOR && TARGET_DIRECT_MOVE && TARGET_POWERPC64 + && TARGET_VSX_SMALL_INTEGER" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rtx result = operands[0]; + rtx input = operands[1]; + rtx di = operands[2]; + + if (MEM_P (input) || altivec_register_operand (input, <QHI:MODE>mode)) + emit_insn (gen_zero_extend<QHI:mode>di2 (di, input)); + else + { + rtx tmp = operands[3]; + if (GET_CODE (tmp) == SCRATCH) + emit_insn (gen_extend<QHI:mode>di2 (di, input)); + else + { + emit_insn (gen_zero_extend<QHI:mode>di2 (tmp, input)); + emit_move_insn (di, tmp); + } + } + + emit_insn (gen_floatdi<FP_ISA3:mode>2 (result, di)); + DONE; +}) + +(define_expand "fix_trunc<mode>si2" + [(set (match_operand:SI 0 "gpc_reg_operand" "") + (fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "")))] + "TARGET_HARD_FLOAT && ((TARGET_FPRS && <TARGET_FLOAT>) || <E500_CONVERT>)" + " +{ + if (!<E500_CONVERT> && !TARGET_VSX_SMALL_INTEGER) + { + rtx src = force_reg (<MODE>mode, operands[1]); + + if (TARGET_STFIWX) + emit_insn (gen_fix_trunc<mode>si2_stfiwx (operands[0], src)); + else + { + rtx tmp = gen_reg_rtx (DImode); + rtx stack = rs6000_allocate_stack_temp (DImode, true, false); + emit_insn (gen_fix_trunc<mode>si2_internal (operands[0], src, + tmp, stack)); + } + DONE; + } +}") + +; Like the convert to float patterns, this insn must be split before +; register allocation so that it can allocate the memory slot if it +; needed +(define_insn_and_split "fix_trunc<mode>si2_stfiwx" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "d"))) + (clobber (match_scratch:DI 2 "=d"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && (<MODE>mode != SFmode || TARGET_SINGLE_FLOAT) + && TARGET_STFIWX && can_create_pseudo_p () + && !TARGET_VSX_SMALL_INTEGER" + "#" + "" + [(pc)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx tmp = operands[2]; + + if (GET_CODE (tmp) == SCRATCH) + tmp = gen_reg_rtx (DImode); + + emit_insn (gen_fctiwz_<mode> (tmp, src)); + if (MEM_P (dest)) + { + dest = rs6000_address_for_fpconvert (dest); + emit_insn (gen_stfiwx (dest, tmp)); + DONE; + } + else if (TARGET_POWERPC64 && (TARGET_MFPGPR || TARGET_DIRECT_MOVE)) + { + dest = gen_lowpart (DImode, dest); + emit_move_insn (dest, tmp); + DONE; + } + else + { + rtx stack = rs6000_allocate_stack_temp (SImode, false, true); + emit_insn (gen_stfiwx (stack, tmp)); + emit_move_insn (dest, stack); + DONE; + } +} + [(set_attr "length" "12") + (set_attr "type" "fp")]) + +(define_insn_and_split "fix_trunc<mode>si2_internal" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r,?r") + (fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "d,<rreg>"))) + (clobber (match_operand:DI 2 "gpc_reg_operand" "=1,d")) + (clobber (match_operand:DI 3 "offsettable_mem_operand" "=o,o"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && !TARGET_VSX_SMALL_INTEGER" + "#" + "" + [(pc)] + " +{ + rtx lowword; + gcc_assert (MEM_P (operands[3])); + lowword = adjust_address (operands[3], SImode, WORDS_BIG_ENDIAN ? 4 : 0); + + emit_insn (gen_fctiwz_<mode> (operands[2], operands[1])); + emit_move_insn (operands[3], operands[2]); + emit_move_insn (operands[0], lowword); + DONE; +}" + [(set_attr "length" "16") + (set_attr "type" "fp")]) + +(define_expand "fix_trunc<mode>di2" + [(set (match_operand:DI 0 "gpc_reg_operand" "") + (fix:DI (match_operand:SFDF 1 "gpc_reg_operand" "")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS + && TARGET_FCFID" + "") + +(define_insn "*fix_trunc<mode>di2_fctidz" + [(set (match_operand:DI 0 "gpc_reg_operand" "=d,wi") + (fix:DI (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv>")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS + && TARGET_FCFID" + "@ + fctidz %0,%1 + xscvdpsxds %x0,%x1" + [(set_attr "type" "fp")]) + +(define_expand "fix_trunc<SFDF:mode><QHI:mode>2" + [(parallel [(set (match_operand:<QHI:MODE> 0 "nonimmediate_operand") + (fix:QHI (match_operand:SFDF 1 "gpc_reg_operand"))) + (clobber (match_scratch:DI 2))])] + "TARGET_P9_VECTOR && TARGET_DIRECT_MOVE_64BIT + && TARGET_VSX_SMALL_INTEGER" +{ + if (MEM_P (operands[0])) + operands[0] = rs6000_address_for_fpconvert (operands[0]); +}) + +(define_insn_and_split "*fix_trunc<SFDF:mode><QHI:mode>2_internal" + [(set (match_operand:<QHI:MODE> 0 "reg_or_indexed_operand" "=wIwJ,rZ") + (fix:QHI + (match_operand:SFDF 1 "gpc_reg_operand" "<SFDF:Fv>,<SFDF:Fv>"))) + (clobber (match_scratch:DI 2 "=X,wi"))] + "TARGET_P9_VECTOR && TARGET_DIRECT_MOVE_64BIT + && TARGET_VSX_SMALL_INTEGER" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + + if (vsx_register_operand (dest, <QHI:MODE>mode)) + { + rtx di_dest = gen_rtx_REG (DImode, REGNO (dest)); + emit_insn (gen_fix_trunc<SFDF:mode>di2 (di_dest, src)); + } + else + { + rtx tmp = operands[2]; + rtx tmp2 = gen_rtx_REG (<QHI:MODE>mode, REGNO (tmp)); + + emit_insn (gen_fix_trunc<SFDF:mode>di2 (tmp, src)); + emit_move_insn (dest, tmp2); + } + DONE; +}) + +(define_expand "fixuns_trunc<mode>si2" + [(set (match_operand:SI 0 "gpc_reg_operand" "") + (unsigned_fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "")))] + "TARGET_HARD_FLOAT + && ((TARGET_FPRS && <TARGET_FLOAT> && TARGET_FCTIWUZ && TARGET_STFIWX) + || <E500_CONVERT>)" + " +{ + if (!<E500_CONVERT> && !TARGET_VSX_SMALL_INTEGER) + { + emit_insn (gen_fixuns_trunc<mode>si2_stfiwx (operands[0], operands[1])); + DONE; + } +}") + +(define_insn_and_split "fixuns_trunc<mode>si2_stfiwx" + [(set (match_operand:SI 0 "nonimmediate_operand" "=rm") + (unsigned_fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "d"))) + (clobber (match_scratch:DI 2 "=d"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT> && TARGET_FCTIWUZ + && TARGET_STFIWX && can_create_pseudo_p () + && !TARGET_VSX_SMALL_INTEGER" + "#" + "" + [(pc)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx tmp = operands[2]; + + if (GET_CODE (tmp) == SCRATCH) + tmp = gen_reg_rtx (DImode); + + emit_insn (gen_fctiwuz_<mode> (tmp, src)); + if (MEM_P (dest)) + { + dest = rs6000_address_for_fpconvert (dest); + emit_insn (gen_stfiwx (dest, tmp)); + DONE; + } + else if (TARGET_POWERPC64 && (TARGET_MFPGPR || TARGET_DIRECT_MOVE)) + { + dest = gen_lowpart (DImode, dest); + emit_move_insn (dest, tmp); + DONE; + } + else + { + rtx stack = rs6000_allocate_stack_temp (SImode, false, true); + emit_insn (gen_stfiwx (stack, tmp)); + emit_move_insn (dest, stack); + DONE; + } +} + [(set_attr "length" "12") + (set_attr "type" "fp")]) + +(define_insn "fixuns_trunc<mode>di2" + [(set (match_operand:DI 0 "gpc_reg_operand" "=d,wi") + (unsigned_fix:DI (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv>")))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS && TARGET_FCTIDUZ" + "@ + fctiduz %0,%1 + xscvdpuxds %x0,%x1" + [(set_attr "type" "fp")]) + +(define_expand "fixuns_trunc<SFDF:mode><QHI:mode>2" + [(parallel [(set (match_operand:<QHI:MODE> 0 "nonimmediate_operand") + (unsigned_fix:QHI (match_operand:SFDF 1 "gpc_reg_operand"))) + (clobber (match_scratch:DI 2))])] + "TARGET_P9_VECTOR && TARGET_DIRECT_MOVE_64BIT + && TARGET_VSX_SMALL_INTEGER" +{ + if (MEM_P (operands[0])) + operands[0] = rs6000_address_for_fpconvert (operands[0]); +}) + +(define_insn_and_split "*fixuns_trunc<SFDF:mode><QHI:mode>2_internal" + [(set (match_operand:<QHI:MODE> 0 "reg_or_indexed_operand" "=wIwJ,rZ") + (unsigned_fix:QHI + (match_operand:SFDF 1 "gpc_reg_operand" "<SFDF:Fv>,<SFDF:Fv>"))) + (clobber (match_scratch:DI 2 "=X,wi"))] + "TARGET_P9_VECTOR && TARGET_DIRECT_MOVE_64BIT + && TARGET_VSX_SMALL_INTEGER" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + + if (vsx_register_operand (dest, <QHI:MODE>mode)) + { + rtx di_dest = gen_rtx_REG (DImode, REGNO (dest)); + emit_insn (gen_fixuns_trunc<SFDF:mode>di2 (di_dest, src)); + } + else + { + rtx tmp = operands[2]; + rtx tmp2 = gen_rtx_REG (<QHI:MODE>mode, REGNO (tmp)); + + emit_insn (gen_fixuns_trunc<SFDF:mode>di2 (tmp, src)); + emit_move_insn (dest, tmp2); + } + DONE; +}) + +;; If -mvsx-small-integer, we can represent the FIX operation directly. On +;; older machines, we have to use an UNSPEC to produce a SImode and move it +;; to another location, since SImode is not allowed in vector registers. +(define_insn "*fctiw<u>z_<mode>_smallint" + [(set (match_operand:SI 0 "vsx_register_operand" "=d,wi") + (any_fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv>")))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && TARGET_VSX_SMALL_INTEGER" + "@ + fctiw<u>z %0,%1 + xscvdp<su>xws %x0,%x1" + [(set_attr "type" "fp")]) + +;; Combiner pattern to prevent moving the result of converting a floating point +;; value to 32-bit integer to GPR in order to save it. +(define_insn_and_split "*fctiw<u>z_<mode>_mem" + [(set (match_operand:SI 0 "memory_operand" "=Z") + (any_fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "wa"))) + (clobber (match_scratch:SI 2 "=wa"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && TARGET_VSX_SMALL_INTEGER" + "#" + "&& reload_completed" + [(set (match_dup 2) + (any_fix:SI (match_dup 1))) + (set (match_dup 0) + (match_dup 2))]) + +;; Here, we use (set (reg) (unspec:DI [(fix:SI ...)] UNSPEC_FCTIWZ)) +;; rather than (set (subreg:SI (reg)) (fix:SI ...)) +;; because the first makes it clear that operand 0 is not live +;; before the instruction. +(define_insn "fctiwz_<mode>" + [(set (match_operand:DI 0 "gpc_reg_operand" "=d,wi") + (unspec:DI [(fix:SI + (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv>"))] + UNSPEC_FCTIWZ))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT" + "@ + fctiwz %0,%1 + xscvdpsxws %x0,%x1" + [(set_attr "type" "fp")]) + +(define_insn "fctiwuz_<mode>" + [(set (match_operand:DI 0 "gpc_reg_operand" "=d,wi") + (unspec:DI [(unsigned_fix:SI + (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv>"))] + UNSPEC_FCTIWUZ))] + "TARGET_HARD_FLOAT && TARGET_FPRS && <TARGET_FLOAT> && TARGET_FCTIWUZ" + "@ + fctiwuz %0,%1 + xscvdpuxws %x0,%x1" + [(set_attr "type" "fp")]) + +;; Only optimize (float (fix x)) -> frz if we are in fast-math mode, since +;; since the friz instruction does not truncate the value if the floating +;; point value is < LONG_MIN or > LONG_MAX. +(define_insn "*friz" + [(set (match_operand:DF 0 "gpc_reg_operand" "=d,ws") + (float:DF (fix:DI (match_operand:DF 1 "gpc_reg_operand" "d,ws"))))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_FPRND + && flag_unsafe_math_optimizations && !flag_trapping_math && TARGET_FRIZ" + "@ + friz %0,%1 + xsrdpiz %x0,%x1" + [(set_attr "type" "fp")]) + +;; Opitmize converting SF/DFmode to signed SImode and back to SF/DFmode. This +;; optimization prevents on ISA 2.06 systems and earlier having to store the +;; value from the FPR/vector unit to the stack, load the value into a GPR, sign +;; extend it, store it back on the stack from the GPR, load it back into the +;; FP/vector unit to do the rounding. If we have direct move (ISA 2.07), +;; disable using store and load to sign/zero extend the value. +(define_insn_and_split "*round32<mode>2_fprs" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=d") + (float:SFDF + (fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "d")))) + (clobber (match_scratch:DI 2 "=d")) + (clobber (match_scratch:DI 3 "=d"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && <SI_CONVERT_FP> && TARGET_LFIWAX && TARGET_STFIWX && TARGET_FCFID + && !TARGET_DIRECT_MOVE && can_create_pseudo_p ()" + "#" + "" + [(pc)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx tmp1 = operands[2]; + rtx tmp2 = operands[3]; + rtx stack = rs6000_allocate_stack_temp (SImode, false, true); + + if (GET_CODE (tmp1) == SCRATCH) + tmp1 = gen_reg_rtx (DImode); + if (GET_CODE (tmp2) == SCRATCH) + tmp2 = gen_reg_rtx (DImode); + + emit_insn (gen_fctiwz_<mode> (tmp1, src)); + emit_insn (gen_stfiwx (stack, tmp1)); + emit_insn (gen_lfiwax (tmp2, stack)); + emit_insn (gen_floatdi<mode>2 (dest, tmp2)); + DONE; +} + [(set_attr "type" "fpload") + (set_attr "length" "16")]) + +(define_insn_and_split "*roundu32<mode>2_fprs" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=d") + (unsigned_float:SFDF + (unsigned_fix:SI (match_operand:SFDF 1 "gpc_reg_operand" "d")))) + (clobber (match_scratch:DI 2 "=d")) + (clobber (match_scratch:DI 3 "=d"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && TARGET_LFIWZX && TARGET_STFIWX && TARGET_FCFIDU && !TARGET_DIRECT_MOVE + && can_create_pseudo_p ()" + "#" + "" + [(pc)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx tmp1 = operands[2]; + rtx tmp2 = operands[3]; + rtx stack = rs6000_allocate_stack_temp (SImode, false, true); + + if (GET_CODE (tmp1) == SCRATCH) + tmp1 = gen_reg_rtx (DImode); + if (GET_CODE (tmp2) == SCRATCH) + tmp2 = gen_reg_rtx (DImode); + + emit_insn (gen_fctiwuz_<mode> (tmp1, src)); + emit_insn (gen_stfiwx (stack, tmp1)); + emit_insn (gen_lfiwzx (tmp2, stack)); + emit_insn (gen_floatdi<mode>2 (dest, tmp2)); + DONE; +} + [(set_attr "type" "fpload") + (set_attr "length" "16")]) + +;; No VSX equivalent to fctid +(define_insn "lrint<mode>di2" + [(set (match_operand:DI 0 "gpc_reg_operand" "=d") + (unspec:DI [(match_operand:SFDF 1 "gpc_reg_operand" "<rreg2>")] + UNSPEC_FCTID))] + "TARGET_<MODE>_FPR && TARGET_FPRND" + "fctid %0,%1" + [(set_attr "type" "fp")]) + +(define_insn "btrunc<mode>2" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv>") + (unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv>")] + UNSPEC_FRIZ))] + "TARGET_<MODE>_FPR && TARGET_FPRND" + "@ + friz %0,%1 + xsrdpiz %x0,%x1" + [(set_attr "type" "fp") + (set_attr "fp_type" "fp_addsub_<Fs>")]) + +(define_insn "ceil<mode>2" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv>") + (unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv>")] + UNSPEC_FRIP))] + "TARGET_<MODE>_FPR && TARGET_FPRND" + "@ + frip %0,%1 + xsrdpip %x0,%x1" + [(set_attr "type" "fp") + (set_attr "fp_type" "fp_addsub_<Fs>")]) + +(define_insn "floor<mode>2" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv>") + (unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv>")] + UNSPEC_FRIM))] + "TARGET_<MODE>_FPR && TARGET_FPRND" + "@ + frim %0,%1 + xsrdpim %x0,%x1" + [(set_attr "type" "fp") + (set_attr "fp_type" "fp_addsub_<Fs>")]) + +;; No VSX equivalent to frin +(define_insn "round<mode>2" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<rreg2>") + (unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<rreg2>")] + UNSPEC_FRIN))] + "TARGET_<MODE>_FPR && TARGET_FPRND" + "frin %0,%1" + [(set_attr "type" "fp") + (set_attr "fp_type" "fp_addsub_<Fs>")]) + +(define_insn "*xsrdpi<mode>2" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Fv>") + (unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "<Fv>")] + UNSPEC_XSRDPI))] + "TARGET_<MODE>_FPR && TARGET_VSX" + "xsrdpi %x0,%x1" + [(set_attr "type" "fp") + (set_attr "fp_type" "fp_addsub_<Fs>")]) + +(define_expand "lround<mode>di2" + [(set (match_dup 2) + (unspec:SFDF [(match_operand:SFDF 1 "gpc_reg_operand" "")] + UNSPEC_XSRDPI)) + (set (match_operand:DI 0 "gpc_reg_operand" "") + (unspec:DI [(match_dup 2)] + UNSPEC_FCTID))] + "TARGET_<MODE>_FPR && TARGET_VSX" +{ + operands[2] = gen_reg_rtx (<MODE>mode); +}) + +; An UNSPEC is used so we don't have to support SImode in FP registers. +; The 'wu' constraint is used for the 2nd alternative to ensure stxsiwx +; is only generated for Power8 or later. +(define_insn "stfiwx" + [(set (match_operand:SI 0 "memory_operand" "=Z,Z") + (unspec:SI [(match_operand:DI 1 "gpc_reg_operand" "d,wu")] + UNSPEC_STFIWX))] + "TARGET_PPC_GFXOPT" + "@ + stfiwx %1,%y0 + stxsiwx %x1,%y0" + [(set_attr "type" "fpstore")]) + +;; If we don't have a direct conversion to single precision, don't enable this +;; conversion for 32-bit without fast math, because we don't have the insn to +;; generate the fixup swizzle to avoid double rounding problems. +(define_expand "floatsisf2" + [(set (match_operand:SF 0 "gpc_reg_operand" "") + (float:SF (match_operand:SI 1 "nonimmediate_operand" "")))] + "TARGET_HARD_FLOAT && TARGET_SINGLE_FLOAT + && (!TARGET_FPRS + || (TARGET_FPRS + && ((TARGET_FCFIDS && TARGET_LFIWAX) + || (TARGET_DOUBLE_FLOAT && TARGET_FCFID + && (TARGET_POWERPC64 || flag_unsafe_math_optimizations)))))" + " +{ + if (!TARGET_FPRS) + { + if (!REG_P (operands[1])) + operands[1] = force_reg (SImode, operands[1]); + } + else if (TARGET_FCFIDS && TARGET_LFIWAX) + { + emit_insn (gen_floatsisf2_lfiwax (operands[0], operands[1])); + DONE; + } + else if (TARGET_FCFID && TARGET_LFIWAX) + { + rtx dfreg = gen_reg_rtx (DFmode); + emit_insn (gen_floatsidf2_lfiwax (dfreg, operands[1])); + emit_insn (gen_truncdfsf2 (operands[0], dfreg)); + DONE; + } + else + { + rtx dreg = operands[1]; + if (!REG_P (dreg)) + dreg = force_reg (SImode, dreg); + dreg = convert_to_mode (DImode, dreg, false); + emit_insn (gen_floatdisf2 (operands[0], dreg)); + DONE; + } +}") + +(define_expand "floatdidf2" + [(set (match_operand:DF 0 "gpc_reg_operand" "") + (float:DF (match_operand:DI 1 "gpc_reg_operand" "")))] + "TARGET_FCFID && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS" + "") + +(define_insn "*floatdidf2_fpr" + [(set (match_operand:DF 0 "gpc_reg_operand" "=d,ws") + (float:DF (match_operand:DI 1 "gpc_reg_operand" "d,wi")))] + "TARGET_FCFID && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS" + "@ + fcfid %0,%1 + xscvsxddp %x0,%x1" + [(set_attr "type" "fp")]) + +; Allow the combiner to merge source memory operands to the conversion so that +; the optimizer/register allocator doesn't try to load the value too early in a +; GPR and then use store/load to move it to a FPR and suffer from a store-load +; hit. We will split after reload to avoid the trip through the GPRs + +(define_insn_and_split "*floatdidf2_mem" + [(set (match_operand:DF 0 "gpc_reg_operand" "=d,ws") + (float:DF (match_operand:DI 1 "memory_operand" "m,Z"))) + (clobber (match_scratch:DI 2 "=d,wi"))] + "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && TARGET_FPRS && TARGET_FCFID" + "#" + "&& reload_completed" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 0) (float:DF (match_dup 2)))] + "" + [(set_attr "length" "8") + (set_attr "type" "fpload")]) + +(define_expand "floatunsdidf2" + [(set (match_operand:DF 0 "gpc_reg_operand" "") + (unsigned_float:DF + (match_operand:DI 1 "gpc_reg_operand" "")))] + "TARGET_HARD_FLOAT && TARGET_FCFIDU" + "") + +(define_insn "*floatunsdidf2_fcfidu" + [(set (match_operand:DF 0 "gpc_reg_operand" "=d,ws") + (unsigned_float:DF (match_operand:DI 1 "gpc_reg_operand" "d,wi")))] + "TARGET_HARD_FLOAT && TARGET_FCFIDU" + "@ + fcfidu %0,%1 + xscvuxddp %x0,%x1" + [(set_attr "type" "fp") + (set_attr "length" "4")]) + +(define_insn_and_split "*floatunsdidf2_mem" + [(set (match_operand:DF 0 "gpc_reg_operand" "=d,ws") + (unsigned_float:DF (match_operand:DI 1 "memory_operand" "m,Z"))) + (clobber (match_scratch:DI 2 "=d,wi"))] + "TARGET_HARD_FLOAT && (TARGET_FCFIDU || VECTOR_UNIT_VSX_P (DFmode))" + "#" + "&& reload_completed" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 0) (unsigned_float:DF (match_dup 2)))] + "" + [(set_attr "length" "8") + (set_attr "type" "fpload")]) + +(define_expand "floatdisf2" + [(set (match_operand:SF 0 "gpc_reg_operand" "") + (float:SF (match_operand:DI 1 "gpc_reg_operand" "")))] + "TARGET_FCFID && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT + && (TARGET_FCFIDS || TARGET_POWERPC64 || flag_unsafe_math_optimizations)" + " +{ + if (!TARGET_FCFIDS) + { + rtx val = operands[1]; + if (!flag_unsafe_math_optimizations) + { + rtx label = gen_label_rtx (); + val = gen_reg_rtx (DImode); + emit_insn (gen_floatdisf2_internal2 (val, operands[1], label)); + emit_label (label); + } + emit_insn (gen_floatdisf2_internal1 (operands[0], val)); + DONE; + } +}") + +(define_insn "floatdisf2_fcfids" + [(set (match_operand:SF 0 "gpc_reg_operand" "=f,wy") + (float:SF (match_operand:DI 1 "gpc_reg_operand" "d,wi")))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT + && TARGET_DOUBLE_FLOAT && TARGET_FCFIDS" + "@ + fcfids %0,%1 + xscvsxdsp %x0,%x1" + [(set_attr "type" "fp")]) + +(define_insn_and_split "*floatdisf2_mem" + [(set (match_operand:SF 0 "gpc_reg_operand" "=f,wy,wy") + (float:SF (match_operand:DI 1 "memory_operand" "m,m,Z"))) + (clobber (match_scratch:DI 2 "=d,d,wi"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT + && TARGET_DOUBLE_FLOAT && TARGET_FCFIDS" + "#" + "&& reload_completed" + [(pc)] + " +{ + emit_move_insn (operands[2], operands[1]); + emit_insn (gen_floatdisf2_fcfids (operands[0], operands[2])); + DONE; +}" + [(set_attr "length" "8")]) + +;; This is not IEEE compliant if rounding mode is "round to nearest". +;; If the DI->DF conversion is inexact, then it's possible to suffer +;; from double rounding. +;; Instead of creating a new cpu type for two FP operations, just use fp +(define_insn_and_split "floatdisf2_internal1" + [(set (match_operand:SF 0 "gpc_reg_operand" "=f") + (float:SF (match_operand:DI 1 "gpc_reg_operand" "d"))) + (clobber (match_scratch:DF 2 "=d"))] + "TARGET_FCFID && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT + && !TARGET_FCFIDS" + "#" + "&& reload_completed" + [(set (match_dup 2) + (float:DF (match_dup 1))) + (set (match_dup 0) + (float_truncate:SF (match_dup 2)))] + "" + [(set_attr "length" "8") + (set_attr "type" "fp")]) + +;; Twiddles bits to avoid double rounding. +;; Bits that might be truncated when converting to DFmode are replaced +;; by a bit that won't be lost at that stage, but is below the SFmode +;; rounding position. +(define_expand "floatdisf2_internal2" + [(parallel [(set (match_dup 3) (ashiftrt:DI (match_operand:DI 1 "" "") + (const_int 53))) + (clobber (reg:DI CA_REGNO))]) + (set (match_operand:DI 0 "" "") (and:DI (match_dup 1) + (const_int 2047))) + (set (match_dup 3) (plus:DI (match_dup 3) + (const_int 1))) + (set (match_dup 0) (plus:DI (match_dup 0) + (const_int 2047))) + (set (match_dup 4) (compare:CCUNS (match_dup 3) + (const_int 2))) + (set (match_dup 0) (ior:DI (match_dup 0) + (match_dup 1))) + (set (match_dup 0) (and:DI (match_dup 0) + (const_int -2048))) + (set (pc) (if_then_else (geu (match_dup 4) (const_int 0)) + (label_ref (match_operand:DI 2 "" "")) + (pc))) + (set (match_dup 0) (match_dup 1))] + "TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT + && !TARGET_FCFIDS" + " +{ + operands[3] = gen_reg_rtx (DImode); + operands[4] = gen_reg_rtx (CCUNSmode); +}") + +(define_expand "floatunsdisf2" + [(set (match_operand:SF 0 "gpc_reg_operand" "") + (unsigned_float:SF (match_operand:DI 1 "gpc_reg_operand" "")))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT + && TARGET_DOUBLE_FLOAT && TARGET_FCFIDUS" + "") + +(define_insn "floatunsdisf2_fcfidus" + [(set (match_operand:SF 0 "gpc_reg_operand" "=f,wu") + (unsigned_float:SF (match_operand:DI 1 "gpc_reg_operand" "d,wi")))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT + && TARGET_DOUBLE_FLOAT && TARGET_FCFIDUS" + "@ + fcfidus %0,%1 + xscvuxdsp %x0,%x1" + [(set_attr "type" "fp")]) + +(define_insn_and_split "*floatunsdisf2_mem" + [(set (match_operand:SF 0 "gpc_reg_operand" "=f,wy,wy") + (unsigned_float:SF (match_operand:DI 1 "memory_operand" "m,m,Z"))) + (clobber (match_scratch:DI 2 "=d,d,wi"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT + && TARGET_DOUBLE_FLOAT && TARGET_FCFIDUS" + "#" + "&& reload_completed" + [(pc)] + " +{ + emit_move_insn (operands[2], operands[1]); + emit_insn (gen_floatunsdisf2_fcfidus (operands[0], operands[2])); + DONE; +}" + [(set_attr "length" "8") + (set_attr "type" "fpload")]) + +;; Define the TImode operations that can be done in a small number +;; of instructions. The & constraints are to prevent the register +;; allocator from allocating registers that overlap with the inputs +;; (for example, having an input in 7,8 and an output in 6,7). We +;; also allow for the output being the same as one of the inputs. + +(define_expand "addti3" + [(set (match_operand:TI 0 "gpc_reg_operand" "") + (plus:TI (match_operand:TI 1 "gpc_reg_operand" "") + (match_operand:TI 2 "reg_or_short_operand" "")))] + "TARGET_64BIT" +{ + rtx lo0 = gen_lowpart (DImode, operands[0]); + rtx lo1 = gen_lowpart (DImode, operands[1]); + rtx lo2 = gen_lowpart (DImode, operands[2]); + rtx hi0 = gen_highpart (DImode, operands[0]); + rtx hi1 = gen_highpart (DImode, operands[1]); + rtx hi2 = gen_highpart_mode (DImode, TImode, operands[2]); + + if (!reg_or_short_operand (lo2, DImode)) + lo2 = force_reg (DImode, lo2); + if (!adde_operand (hi2, DImode)) + hi2 = force_reg (DImode, hi2); + + emit_insn (gen_adddi3_carry (lo0, lo1, lo2)); + emit_insn (gen_adddi3_carry_in (hi0, hi1, hi2)); + DONE; +}) + +(define_expand "subti3" + [(set (match_operand:TI 0 "gpc_reg_operand" "") + (minus:TI (match_operand:TI 1 "reg_or_short_operand" "") + (match_operand:TI 2 "gpc_reg_operand" "")))] + "TARGET_64BIT" +{ + rtx lo0 = gen_lowpart (DImode, operands[0]); + rtx lo1 = gen_lowpart (DImode, operands[1]); + rtx lo2 = gen_lowpart (DImode, operands[2]); + rtx hi0 = gen_highpart (DImode, operands[0]); + rtx hi1 = gen_highpart_mode (DImode, TImode, operands[1]); + rtx hi2 = gen_highpart (DImode, operands[2]); + + if (!reg_or_short_operand (lo1, DImode)) + lo1 = force_reg (DImode, lo1); + if (!adde_operand (hi1, DImode)) + hi1 = force_reg (DImode, hi1); + + emit_insn (gen_subfdi3_carry (lo0, lo2, lo1)); + emit_insn (gen_subfdi3_carry_in (hi0, hi2, hi1)); + DONE; +}) + +;; 128-bit logical operations expanders + +(define_expand "and<mode>3" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "") + (and:BOOL_128 (match_operand:BOOL_128 1 "vlogical_operand" "") + (match_operand:BOOL_128 2 "vlogical_operand" "")))] + "" + "") + +(define_expand "ior<mode>3" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "") + (ior:BOOL_128 (match_operand:BOOL_128 1 "vlogical_operand" "") + (match_operand:BOOL_128 2 "vlogical_operand" "")))] + "" + "") + +(define_expand "xor<mode>3" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "") + (xor:BOOL_128 (match_operand:BOOL_128 1 "vlogical_operand" "") + (match_operand:BOOL_128 2 "vlogical_operand" "")))] + "" + "") + +(define_expand "one_cmpl<mode>2" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "") + (not:BOOL_128 (match_operand:BOOL_128 1 "vlogical_operand" "")))] + "" + "") + +(define_expand "nor<mode>3" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "") + (and:BOOL_128 + (not:BOOL_128 (match_operand:BOOL_128 1 "vlogical_operand" "")) + (not:BOOL_128 (match_operand:BOOL_128 2 "vlogical_operand" ""))))] + "" + "") + +(define_expand "andc<mode>3" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "") + (and:BOOL_128 + (not:BOOL_128 (match_operand:BOOL_128 2 "vlogical_operand" "")) + (match_operand:BOOL_128 1 "vlogical_operand" "")))] + "" + "") + +;; Power8 vector logical instructions. +(define_expand "eqv<mode>3" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "") + (not:BOOL_128 + (xor:BOOL_128 (match_operand:BOOL_128 1 "vlogical_operand" "") + (match_operand:BOOL_128 2 "vlogical_operand" ""))))] + "<MODE>mode == TImode || <MODE>mode == PTImode || TARGET_P8_VECTOR" + "") + +;; Rewrite nand into canonical form +(define_expand "nand<mode>3" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "") + (ior:BOOL_128 + (not:BOOL_128 (match_operand:BOOL_128 1 "vlogical_operand" "")) + (not:BOOL_128 (match_operand:BOOL_128 2 "vlogical_operand" ""))))] + "<MODE>mode == TImode || <MODE>mode == PTImode || TARGET_P8_VECTOR" + "") + +;; The canonical form is to have the negated element first, so we need to +;; reverse arguments. +(define_expand "orc<mode>3" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "") + (ior:BOOL_128 + (not:BOOL_128 (match_operand:BOOL_128 2 "vlogical_operand" "")) + (match_operand:BOOL_128 1 "vlogical_operand" "")))] + "<MODE>mode == TImode || <MODE>mode == PTImode || TARGET_P8_VECTOR" + "") + +;; 128-bit logical operations insns and split operations +(define_insn_and_split "*and<mode>3_internal" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "=<BOOL_REGS_OUTPUT>") + (and:BOOL_128 + (match_operand:BOOL_128 1 "vlogical_operand" "%<BOOL_REGS_OP1>") + (match_operand:BOOL_128 2 "vlogical_operand" "<BOOL_REGS_OP2>")))] + "" +{ + if (TARGET_VSX && vsx_register_operand (operands[0], <MODE>mode)) + return "xxland %x0,%x1,%x2"; + + if (TARGET_ALTIVEC && altivec_register_operand (operands[0], <MODE>mode)) + return "vand %0,%1,%2"; + + return "#"; +} + "reload_completed && int_reg_operand (operands[0], <MODE>mode)" + [(const_int 0)] +{ + rs6000_split_logical (operands, AND, false, false, false); + DONE; +} + [(set (attr "type") + (if_then_else + (match_test "vsx_register_operand (operands[0], <MODE>mode)") + (const_string "veclogical") + (const_string "integer"))) + (set (attr "length") + (if_then_else + (match_test "vsx_register_operand (operands[0], <MODE>mode)") + (const_string "4") + (if_then_else + (match_test "TARGET_POWERPC64") + (const_string "8") + (const_string "16"))))]) + +;; 128-bit IOR/XOR +(define_insn_and_split "*bool<mode>3_internal" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "=<BOOL_REGS_OUTPUT>") + (match_operator:BOOL_128 3 "boolean_or_operator" + [(match_operand:BOOL_128 1 "vlogical_operand" "%<BOOL_REGS_OP1>") + (match_operand:BOOL_128 2 "vlogical_operand" "<BOOL_REGS_OP2>")]))] + "" +{ + if (TARGET_VSX && vsx_register_operand (operands[0], <MODE>mode)) + return "xxl%q3 %x0,%x1,%x2"; + + if (TARGET_ALTIVEC && altivec_register_operand (operands[0], <MODE>mode)) + return "v%q3 %0,%1,%2"; + + return "#"; +} + "reload_completed && int_reg_operand (operands[0], <MODE>mode)" + [(const_int 0)] +{ + rs6000_split_logical (operands, GET_CODE (operands[3]), false, false, false); + DONE; +} + [(set (attr "type") + (if_then_else + (match_test "vsx_register_operand (operands[0], <MODE>mode)") + (const_string "veclogical") + (const_string "integer"))) + (set (attr "length") + (if_then_else + (match_test "vsx_register_operand (operands[0], <MODE>mode)") + (const_string "4") + (if_then_else + (match_test "TARGET_POWERPC64") + (const_string "8") + (const_string "16"))))]) + +;; 128-bit ANDC/ORC +(define_insn_and_split "*boolc<mode>3_internal1" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "=<BOOL_REGS_OUTPUT>") + (match_operator:BOOL_128 3 "boolean_operator" + [(not:BOOL_128 + (match_operand:BOOL_128 2 "vlogical_operand" "<BOOL_REGS_OP2>")) + (match_operand:BOOL_128 1 "vlogical_operand" "<BOOL_REGS_OP1>")]))] + "TARGET_P8_VECTOR || (GET_CODE (operands[3]) == AND)" +{ + if (TARGET_VSX && vsx_register_operand (operands[0], <MODE>mode)) + return "xxl%q3 %x0,%x1,%x2"; + + if (TARGET_ALTIVEC && altivec_register_operand (operands[0], <MODE>mode)) + return "v%q3 %0,%1,%2"; + + return "#"; +} + "(TARGET_P8_VECTOR || (GET_CODE (operands[3]) == AND)) + && reload_completed && int_reg_operand (operands[0], <MODE>mode)" + [(const_int 0)] +{ + rs6000_split_logical (operands, GET_CODE (operands[3]), false, false, true); + DONE; +} + [(set (attr "type") + (if_then_else + (match_test "vsx_register_operand (operands[0], <MODE>mode)") + (const_string "veclogical") + (const_string "integer"))) + (set (attr "length") + (if_then_else + (match_test "vsx_register_operand (operands[0], <MODE>mode)") + (const_string "4") + (if_then_else + (match_test "TARGET_POWERPC64") + (const_string "8") + (const_string "16"))))]) + +(define_insn_and_split "*boolc<mode>3_internal2" + [(set (match_operand:TI2 0 "int_reg_operand" "=&r,r,r") + (match_operator:TI2 3 "boolean_operator" + [(not:TI2 + (match_operand:TI2 2 "int_reg_operand" "r,0,r")) + (match_operand:TI2 1 "int_reg_operand" "r,r,0")]))] + "!TARGET_P8_VECTOR && (GET_CODE (operands[3]) != AND)" + "#" + "reload_completed && !TARGET_P8_VECTOR && (GET_CODE (operands[3]) != AND)" + [(const_int 0)] +{ + rs6000_split_logical (operands, GET_CODE (operands[3]), false, false, true); + DONE; +} + [(set_attr "type" "integer") + (set (attr "length") + (if_then_else + (match_test "TARGET_POWERPC64") + (const_string "8") + (const_string "16")))]) + +;; 128-bit NAND/NOR +(define_insn_and_split "*boolcc<mode>3_internal1" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "=<BOOL_REGS_OUTPUT>") + (match_operator:BOOL_128 3 "boolean_operator" + [(not:BOOL_128 + (match_operand:BOOL_128 1 "vlogical_operand" "<BOOL_REGS_OP1>")) + (not:BOOL_128 + (match_operand:BOOL_128 2 "vlogical_operand" "<BOOL_REGS_OP2>"))]))] + "TARGET_P8_VECTOR || (GET_CODE (operands[3]) == AND)" +{ + if (TARGET_VSX && vsx_register_operand (operands[0], <MODE>mode)) + return "xxl%q3 %x0,%x1,%x2"; + + if (TARGET_ALTIVEC && altivec_register_operand (operands[0], <MODE>mode)) + return "v%q3 %0,%1,%2"; + + return "#"; +} + "(TARGET_P8_VECTOR || (GET_CODE (operands[3]) == AND)) + && reload_completed && int_reg_operand (operands[0], <MODE>mode)" + [(const_int 0)] +{ + rs6000_split_logical (operands, GET_CODE (operands[3]), false, true, true); + DONE; +} + [(set (attr "type") + (if_then_else + (match_test "vsx_register_operand (operands[0], <MODE>mode)") + (const_string "veclogical") + (const_string "integer"))) + (set (attr "length") + (if_then_else + (match_test "vsx_register_operand (operands[0], <MODE>mode)") + (const_string "4") + (if_then_else + (match_test "TARGET_POWERPC64") + (const_string "8") + (const_string "16"))))]) + +(define_insn_and_split "*boolcc<mode>3_internal2" + [(set (match_operand:TI2 0 "int_reg_operand" "=&r,r,r") + (match_operator:TI2 3 "boolean_operator" + [(not:TI2 + (match_operand:TI2 1 "int_reg_operand" "r,0,r")) + (not:TI2 + (match_operand:TI2 2 "int_reg_operand" "r,r,0"))]))] + "!TARGET_P8_VECTOR && (GET_CODE (operands[3]) != AND)" + "#" + "reload_completed && !TARGET_P8_VECTOR && (GET_CODE (operands[3]) != AND)" + [(const_int 0)] +{ + rs6000_split_logical (operands, GET_CODE (operands[3]), false, true, true); + DONE; +} + [(set_attr "type" "integer") + (set (attr "length") + (if_then_else + (match_test "TARGET_POWERPC64") + (const_string "8") + (const_string "16")))]) + + +;; 128-bit EQV +(define_insn_and_split "*eqv<mode>3_internal1" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "=<BOOL_REGS_OUTPUT>") + (not:BOOL_128 + (xor:BOOL_128 + (match_operand:BOOL_128 1 "vlogical_operand" "<BOOL_REGS_OP1>") + (match_operand:BOOL_128 2 "vlogical_operand" "<BOOL_REGS_OP2>"))))] + "TARGET_P8_VECTOR" +{ + if (vsx_register_operand (operands[0], <MODE>mode)) + return "xxleqv %x0,%x1,%x2"; + + return "#"; +} + "TARGET_P8_VECTOR && reload_completed + && int_reg_operand (operands[0], <MODE>mode)" + [(const_int 0)] +{ + rs6000_split_logical (operands, XOR, true, false, false); + DONE; +} + [(set (attr "type") + (if_then_else + (match_test "vsx_register_operand (operands[0], <MODE>mode)") + (const_string "veclogical") + (const_string "integer"))) + (set (attr "length") + (if_then_else + (match_test "vsx_register_operand (operands[0], <MODE>mode)") + (const_string "4") + (if_then_else + (match_test "TARGET_POWERPC64") + (const_string "8") + (const_string "16"))))]) + +(define_insn_and_split "*eqv<mode>3_internal2" + [(set (match_operand:TI2 0 "int_reg_operand" "=&r,r,r") + (not:TI2 + (xor:TI2 + (match_operand:TI2 1 "int_reg_operand" "r,0,r") + (match_operand:TI2 2 "int_reg_operand" "r,r,0"))))] + "!TARGET_P8_VECTOR" + "#" + "reload_completed && !TARGET_P8_VECTOR" + [(const_int 0)] +{ + rs6000_split_logical (operands, XOR, true, false, false); + DONE; +} + [(set_attr "type" "integer") + (set (attr "length") + (if_then_else + (match_test "TARGET_POWERPC64") + (const_string "8") + (const_string "16")))]) + +;; 128-bit one's complement +(define_insn_and_split "*one_cmpl<mode>3_internal" + [(set (match_operand:BOOL_128 0 "vlogical_operand" "=<BOOL_REGS_OUTPUT>") + (not:BOOL_128 + (match_operand:BOOL_128 1 "vlogical_operand" "<BOOL_REGS_UNARY>")))] + "" +{ + if (TARGET_VSX && vsx_register_operand (operands[0], <MODE>mode)) + return "xxlnor %x0,%x1,%x1"; + + if (TARGET_ALTIVEC && altivec_register_operand (operands[0], <MODE>mode)) + return "vnor %0,%1,%1"; + + return "#"; +} + "reload_completed && int_reg_operand (operands[0], <MODE>mode)" + [(const_int 0)] +{ + rs6000_split_logical (operands, NOT, false, false, false); + DONE; +} + [(set (attr "type") + (if_then_else + (match_test "vsx_register_operand (operands[0], <MODE>mode)") + (const_string "veclogical") + (const_string "integer"))) + (set (attr "length") + (if_then_else + (match_test "vsx_register_operand (operands[0], <MODE>mode)") + (const_string "4") + (if_then_else + (match_test "TARGET_POWERPC64") + (const_string "8") + (const_string "16"))))]) + + +;; Now define ways of moving data around. + +;; Set up a register with a value from the GOT table + +(define_expand "movsi_got" + [(set (match_operand:SI 0 "gpc_reg_operand" "") + (unspec:SI [(match_operand:SI 1 "got_operand" "") + (match_dup 2)] UNSPEC_MOVSI_GOT))] + "DEFAULT_ABI == ABI_V4 && flag_pic == 1" + " +{ + if (GET_CODE (operands[1]) == CONST) + { + rtx offset = const0_rtx; + HOST_WIDE_INT value; + + operands[1] = eliminate_constant_term (XEXP (operands[1], 0), &offset); + value = INTVAL (offset); + if (value != 0) + { + rtx tmp = (!can_create_pseudo_p () + ? operands[0] + : gen_reg_rtx (Pmode)); + emit_insn (gen_movsi_got (tmp, operands[1])); + emit_insn (gen_addsi3 (operands[0], tmp, offset)); + DONE; + } + } + + operands[2] = rs6000_got_register (operands[1]); +}") + +(define_insn "*movsi_got_internal" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (unspec:SI [(match_operand:SI 1 "got_no_const_operand" "") + (match_operand:SI 2 "gpc_reg_operand" "b")] + UNSPEC_MOVSI_GOT))] + "DEFAULT_ABI == ABI_V4 && flag_pic == 1" + "lwz %0,%a1@got(%2)" + [(set_attr "type" "load")]) + +;; Used by sched, shorten_branches and final when the GOT pseudo reg +;; didn't get allocated to a hard register. +(define_split + [(set (match_operand:SI 0 "gpc_reg_operand" "") + (unspec:SI [(match_operand:SI 1 "got_no_const_operand" "") + (match_operand:SI 2 "memory_operand" "")] + UNSPEC_MOVSI_GOT))] + "DEFAULT_ABI == ABI_V4 + && flag_pic == 1 + && (reload_in_progress || reload_completed)" + [(set (match_dup 0) (match_dup 2)) + (set (match_dup 0) (unspec:SI [(match_dup 1)(match_dup 0)] + UNSPEC_MOVSI_GOT))] + "") + +;; For SI, we special-case integers that can't be loaded in one insn. We +;; do the load 16-bits at a time. We could do this by loading from memory, +;; and this is even supposed to be faster, but it is simpler not to get +;; integers in the TOC. +(define_insn "movsi_low" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mem:SI (lo_sum:SI (match_operand:SI 1 "gpc_reg_operand" "b") + (match_operand 2 "" ""))))] + "TARGET_MACHO && ! TARGET_64BIT" + "lwz %0,lo16(%2)(%1)" + [(set_attr "type" "load") + (set_attr "length" "4")]) + +;; MR LA LWZ LFIWZX LXSIWZX +;; STW STFIWX STXSIWX LI LIS +;; # XXLOR XXSPLTIB 0 XXSPLTIB -1 VSPLTISW +;; XXLXOR 0 XXLORC -1 P9 const MTVSRWZ MFVSRWZ +;; MF%1 MT%0 MT%0 NOP +(define_insn "*movsi_internal1" + [(set (match_operand:SI 0 "rs6000_nonimmediate_operand" + "=r, r, r, ?*wI, ?*wH, + m, ?Z, ?Z, r, r, + r, ?*wIwH, ?*wJwK, ?*wJwK, ?*wu, + ?*wJwK, ?*wH, ?*wK, ?*wIwH, ?r, + r, *c*l, *h, *h") + + (match_operand:SI 1 "input_operand" + "r, U, m, Z, Z, + r, wI, wH, I, L, + n, wIwH, O, wM, wB, + O, wM, wS, r, wIwH, + *h, r, r, 0"))] + + "!TARGET_SINGLE_FPU && + (gpc_reg_operand (operands[0], SImode) || gpc_reg_operand (operands[1], SImode))" + "@ + mr %0,%1 + la %0,%a1 + lwz%U1%X1 %0,%1 + lfiwzx %0,%y1 + lxsiwzx %x0,%y1 + stw%U0%X0 %1,%0 + stfiwx %1,%y0 + stxsiwx %x1,%y0 + li %0,%1 + lis %0,%v1 + # + xxlor %x0,%x1,%x1 + xxspltib %x0,0 + xxspltib %x0,255 + vspltisw %0,%1 + xxlxor %x0,%x0,%x0 + xxlorc %x0,%x0,%x0 + # + mtvsrwz %x0,%1 + mfvsrwz %0,%x1 + mf%1 %0 + mt%0 %1 + mt%0 %1 + nop" + [(set_attr "type" + "*, *, load, fpload, fpload, + store, fpstore, fpstore, *, *, + *, veclogical, vecsimple, vecsimple, vecsimple, + veclogical, veclogical, vecsimple, mffgpr, mftgpr, + *, *, *, *") + + (set_attr "length" + "4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, + 8, 4, 4, 4, 4, + 4, 4, 8, 4, 4, + 4, 4, 4, 4")]) + +(define_insn "*movsi_internal1_single" + [(set (match_operand:SI 0 "rs6000_nonimmediate_operand" "=r,r,r,m,r,r,r,r,*c*l,*h,*h,m,*f") + (match_operand:SI 1 "input_operand" "r,U,m,r,I,L,n,*h,r,r,0,f,m"))] + "TARGET_SINGLE_FPU && + (gpc_reg_operand (operands[0], SImode) || gpc_reg_operand (operands[1], SImode))" + "@ + mr %0,%1 + la %0,%a1 + lwz%U1%X1 %0,%1 + stw%U0%X0 %1,%0 + li %0,%1 + lis %0,%v1 + # + mf%1 %0 + mt%0 %1 + mt%0 %1 + nop + stfs%U0%X0 %1,%0 + lfs%U1%X1 %0,%1" + [(set_attr "type" "*,*,load,store,*,*,*,mfjmpr,mtjmpr,*,*,fpstore,fpload") + (set_attr "length" "4,4,4,4,4,4,8,4,4,4,4,4,4")]) + +;; Like movsi, but adjust a SF value to be used in a SI context, i.e. +;; (set (reg:SI ...) (subreg:SI (reg:SF ...) 0)) +;; +;; Because SF values are actually stored as DF values within the vector +;; registers, we need to convert the value to the vector SF format when +;; we need to use the bits in a union or similar cases. We only need +;; to do this transformation when the value is a vector register. Loads, +;; stores, and transfers within GPRs are assumed to be safe. +;; +;; This is a more general case of reload_gpr_from_vsxsf. That insn must have +;; no alternatives, because the call is created as part of secondary_reload, +;; and operand #2's register class is used to allocate the temporary register. +;; This function is called before reload, and it creates the temporary as +;; needed. + +;; MR LWZ LFIWZX LXSIWZX STW +;; STFS STXSSP STXSSPX VSX->GPR MTVSRWZ +;; VSX->VSX + +(define_insn_and_split "movsi_from_sf" + [(set (match_operand:SI 0 "rs6000_nonimmediate_operand" + "=r, r, ?*wI, ?*wH, m, + m, wY, Z, r, wIwH, + ?wK") + + (unspec:SI [(match_operand:SF 1 "input_operand" + "r, m, Z, Z, r, + f, wb, wu, wIwH, r, + wK")] + UNSPEC_SI_FROM_SF)) + + (clobber (match_scratch:V4SF 2 + "=X, X, X, X, X, + X, X, X, wa, X, + wa"))] + + "TARGET_NO_SF_SUBREG + && (register_operand (operands[0], SImode) + || register_operand (operands[1], SFmode))" + "@ + mr %0,%1 + lwz%U1%X1 %0,%1 + lfiwzx %0,%y1 + lxsiwzx %x0,%y1 + stw%U0%X0 %1,%0 + stfs%U0%X0 %1,%0 + stxssp %1,%0 + stxsspx %x1,%y0 + # + mtvsrwz %x0,%1 + #" + "&& reload_completed + && register_operand (operands[0], SImode) + && vsx_reg_sfsubreg_ok (operands[1], SFmode)" + [(const_int 0)] +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + rtx op2 = operands[2]; + rtx op0_di = gen_rtx_REG (DImode, REGNO (op0)); + + emit_insn (gen_vsx_xscvdpspn_scalar (op2, op1)); + + if (int_reg_operand (op0, SImode)) + { + emit_insn (gen_p8_mfvsrd_4_disf (op0_di, op2)); + emit_insn (gen_lshrdi3 (op0_di, op0_di, GEN_INT (32))); + } + else + { + rtx op1_v16qi = gen_rtx_REG (V16QImode, REGNO (op1)); + rtx byte_off = VECTOR_ELT_ORDER_BIG ? const0_rtx : GEN_INT (12); + emit_insn (gen_vextract4b (op0_di, op1_v16qi, byte_off)); + } + + DONE; +} + [(set_attr "type" + "*, load, fpload, fpload, store, + fpstore, fpstore, fpstore, mftgpr, mffgpr, + veclogical") + + (set_attr "length" + "4, 4, 4, 4, 4, + 4, 4, 4, 12, 4, + 8")]) + +;; movsi_from_sf with zero extension +;; +;; RLDICL LWZ LFIWZX LXSIWZX VSX->GPR +;; MTVSRWZ VSX->VSX + +(define_insn_and_split "*movdi_from_sf_zero_ext" + [(set (match_operand:DI 0 "gpc_reg_operand" + "=r, r, ?*wI, ?*wH, r, + wIwH, ?wK") + + (zero_extend:DI + (unspec:SI [(match_operand:SF 1 "input_operand" + "r, m, Z, Z, wIwH, + r, wK")] + UNSPEC_SI_FROM_SF))) + + (clobber (match_scratch:V4SF 2 + "=X, X, X, X, wa, + X, wa"))] + + "TARGET_DIRECT_MOVE_64BIT + && (register_operand (operands[0], DImode) + || register_operand (operands[1], SImode))" + "@ + rldicl %0,%1,0,32 + lwz%U1%X1 %0,%1 + lfiwzx %0,%y1 + lxsiwzx %x0,%y1 + # + mtvsrwz %x0,%1 + #" + "&& reload_completed + && vsx_reg_sfsubreg_ok (operands[1], SFmode)" + [(const_int 0)] +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + rtx op2 = operands[2]; + + emit_insn (gen_vsx_xscvdpspn_scalar (op2, op1)); + + if (int_reg_operand (op0, DImode)) + { + emit_insn (gen_p8_mfvsrd_4_disf (op0, op2)); + emit_insn (gen_lshrdi3 (op0, op0, GEN_INT (32))); + } + else + { + rtx op0_si = gen_rtx_REG (SImode, REGNO (op0)); + rtx op1_v16qi = gen_rtx_REG (V16QImode, REGNO (op1)); + rtx byte_off = VECTOR_ELT_ORDER_BIG ? const0_rtx : GEN_INT (12); + emit_insn (gen_vextract4b (op0_si, op1_v16qi, byte_off)); + } + + DONE; +} + [(set_attr "type" + "*, load, fpload, fpload, mftgpr, + mffgpr, veclogical") + + (set_attr "length" + "4, 4, 4, 4, 12, + 4, 8")]) + +;; Split a load of a large constant into the appropriate two-insn +;; sequence. + +(define_split + [(set (match_operand:SI 0 "gpc_reg_operand" "") + (match_operand:SI 1 "const_int_operand" ""))] + "(unsigned HOST_WIDE_INT) (INTVAL (operands[1]) + 0x8000) >= 0x10000 + && (INTVAL (operands[1]) & 0xffff) != 0" + [(set (match_dup 0) + (match_dup 2)) + (set (match_dup 0) + (ior:SI (match_dup 0) + (match_dup 3)))] + " +{ + if (rs6000_emit_set_const (operands[0], operands[1])) + DONE; + else + FAIL; +}") + +;; Split loading -128..127 to use XXSPLITB and VEXTSW2D +(define_split + [(set (match_operand:DI 0 "altivec_register_operand") + (match_operand:DI 1 "xxspltib_constant_split"))] + "TARGET_VSX_SMALL_INTEGER && TARGET_P9_VECTOR && reload_completed" + [(const_int 0)] +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + int r = REGNO (op0); + rtx op0_v16qi = gen_rtx_REG (V16QImode, r); + + emit_insn (gen_xxspltib_v16qi (op0_v16qi, op1)); + emit_insn (gen_vsx_sign_extend_qi_si (operands[0], op0_v16qi)); + DONE; +}) + +(define_insn "*mov<mode>_internal2" + [(set (match_operand:CC 2 "cc_reg_operand" "=y,x,?y") + (compare:CC (match_operand:P 1 "gpc_reg_operand" "0,r,r") + (const_int 0))) + (set (match_operand:P 0 "gpc_reg_operand" "=r,r,r") (match_dup 1))] + "" + "@ + cmp<wd>i %2,%0,0 + mr. %0,%1 + #" + [(set_attr "type" "cmp,logical,cmp") + (set_attr "dot" "yes") + (set_attr "length" "4,4,8")]) + +(define_split + [(set (match_operand:CC 2 "cc_reg_not_micro_cr0_operand" "") + (compare:CC (match_operand:P 1 "gpc_reg_operand" "") + (const_int 0))) + (set (match_operand:P 0 "gpc_reg_operand" "") (match_dup 1))] + "reload_completed" + [(set (match_dup 0) (match_dup 1)) + (set (match_dup 2) + (compare:CC (match_dup 0) + (const_int 0)))] + "") + +(define_expand "mov<mode>" + [(set (match_operand:INT 0 "general_operand" "") + (match_operand:INT 1 "any_operand" ""))] + "" + "{ rs6000_emit_move (operands[0], operands[1], <MODE>mode); DONE; }") + +;; MR LHZ/LBZ LXSI*ZX STH/STB STXSI*X LI +;; XXLOR load 0 load -1 VSPLTI* # MFVSRWZ +;; MTVSRWZ MF%1 MT%1 NOP +(define_insn "*mov<mode>_internal" + [(set (match_operand:QHI 0 "nonimmediate_operand" + "=r, r, ?*wJwK, m, Z, r, + ?*wJwK, ?*wJwK, ?*wJwK, ?*wK, ?*wK, r, + ?*wJwK, r, *c*l, *h") + + (match_operand:QHI 1 "input_operand" + "r, m, Z, r, wJwK, i, + wJwK, O, wM, wB, wS, ?*wJwK, + r, *h, r, 0"))] + + "gpc_reg_operand (operands[0], <MODE>mode) + || gpc_reg_operand (operands[1], <MODE>mode)" + "@ + mr %0,%1 + l<wd>z%U1%X1 %0,%1 + lxsi<wd>zx %x0,%y1 + st<wd>%U0%X0 %1,%0 + stxsi<wd>x %x1,%y0 + li %0,%1 + xxlor %x0,%x1,%x1 + xxspltib %x0,0 + xxspltib %x0,255 + vspltis<wd> %0,%1 + # + mfvsrwz %0,%x1 + mtvsrwz %x0,%1 + mf%1 %0 + mt%0 %1 + nop" + [(set_attr "type" + "*, load, fpload, store, fpstore, *, + vecsimple, vecperm, vecperm, vecperm, vecperm, mftgpr, + mffgpr, mfjmpr, mtjmpr, *") + + (set_attr "length" + "4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 8, 4, + 4, 4, 4, 4")]) + + +;; Here is how to move condition codes around. When we store CC data in +;; an integer register or memory, we store just the high-order 4 bits. +;; This lets us not shift in the most common case of CR0. +(define_expand "movcc" + [(set (match_operand:CC 0 "nonimmediate_operand" "") + (match_operand:CC 1 "nonimmediate_operand" ""))] + "" + "") + +(define_insn "*movcc_internal1" + [(set (match_operand:CC 0 "nonimmediate_operand" + "=y,x,?y,y,r,r,r,r,r,*c*l,r,m") + (match_operand:CC 1 "general_operand" + " y,r, r,O,x,y,r,I,h, r,m,r"))] + "register_operand (operands[0], CCmode) + || register_operand (operands[1], CCmode)" + "@ + mcrf %0,%1 + mtcrf 128,%1 + rlwinm %1,%1,%F0,0xffffffff\;mtcrf %R0,%1\;rlwinm %1,%1,%f0,0xffffffff + crxor %0,%0,%0 + mfcr %0%Q1 + mfcr %0%Q1\;rlwinm %0,%0,%f1,0xf0000000 + mr %0,%1 + li %0,%1 + mf%1 %0 + mt%0 %1 + lwz%U1%X1 %0,%1 + stw%U0%X0 %1,%0" + [(set (attr "type") + (cond [(eq_attr "alternative" "0,3") + (const_string "cr_logical") + (eq_attr "alternative" "1,2") + (const_string "mtcr") + (eq_attr "alternative" "6,7") + (const_string "integer") + (eq_attr "alternative" "8") + (const_string "mfjmpr") + (eq_attr "alternative" "9") + (const_string "mtjmpr") + (eq_attr "alternative" "10") + (const_string "load") + (eq_attr "alternative" "11") + (const_string "store") + (match_test "TARGET_MFCRF") + (const_string "mfcrf") + ] + (const_string "mfcr"))) + (set_attr "length" "4,4,12,4,4,8,4,4,4,4,4,4")]) + +;; For floating-point, we normally deal with the floating-point registers +;; unless -msoft-float is used. The sole exception is that parameter passing +;; can produce floating-point values in fixed-point registers. Unless the +;; value is a simple constant or already in memory, we deal with this by +;; allocating memory and copying the value explicitly via that memory location. + +;; Move 32-bit binary/decimal floating point +(define_expand "mov<mode>" + [(set (match_operand:FMOVE32 0 "nonimmediate_operand" "") + (match_operand:FMOVE32 1 "any_operand" ""))] + "<fmove_ok>" + "{ rs6000_emit_move (operands[0], operands[1], <MODE>mode); DONE; }") + +(define_split + [(set (match_operand:FMOVE32 0 "gpc_reg_operand" "") + (match_operand:FMOVE32 1 "const_double_operand" ""))] + "reload_completed + && ((GET_CODE (operands[0]) == REG && REGNO (operands[0]) <= 31) + || (GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == REG + && REGNO (SUBREG_REG (operands[0])) <= 31))" + [(set (match_dup 2) (match_dup 3))] + " +{ + long l; + + <real_value_to_target> (*CONST_DOUBLE_REAL_VALUE (operands[1]), l); + + if (! TARGET_POWERPC64) + operands[2] = operand_subword (operands[0], 0, 0, <MODE>mode); + else + operands[2] = gen_lowpart (SImode, operands[0]); + + operands[3] = gen_int_mode (l, SImode); +}") + +;; Originally, we tried to keep movsf and movsd common, but the differences +;; addressing was making it rather difficult to hide with mode attributes. In +;; particular for SFmode, on ISA 2.07 (power8) systems, having the GPR store +;; before the VSX stores meant that the register allocator would tend to do a +;; direct move to the GPR (which involves conversion from scalar to +;; vector/memory formats) to save values in the traditional Altivec registers, +;; while SDmode had problems on power6 if the GPR store was not first due to +;; the power6 not having an integer store operation. +;; +;; LWZ LFS LXSSP LXSSPX STFS STXSSP +;; STXSSPX STW XXLXOR LI FMR XSCPSGNDP +;; MR MT<x> MF<x> NOP + +(define_insn "movsf_hardfloat" + [(set (match_operand:SF 0 "nonimmediate_operand" + "=!r, f, wb, wu, m, wY, + Z, m, ww, !r, f, ww, + !r, *c*l, !r, *h") + (match_operand:SF 1 "input_operand" + "m, m, wY, Z, f, wb, + wu, r, j, j, f, ww, + r, r, *h, 0"))] + "(register_operand (operands[0], SFmode) + || register_operand (operands[1], SFmode)) + && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT + && (TARGET_ALLOW_SF_SUBREG + || valid_sf_si_move (operands[0], operands[1], SFmode))" + "@ + lwz%U1%X1 %0,%1 + lfs%U1%X1 %0,%1 + lxssp %0,%1 + lxsspx %x0,%y1 + stfs%U0%X0 %1,%0 + stxssp %1,%0 + stxsspx %x1,%y0 + stw%U0%X0 %1,%0 + xxlxor %x0,%x0,%x0 + li %0,0 + fmr %0,%1 + xscpsgndp %x0,%x1,%x1 + mr %0,%1 + mt%0 %1 + mf%1 %0 + nop" + [(set_attr "type" + "load, fpload, fpload, fpload, fpstore, fpstore, + fpstore, store, veclogical, integer, fpsimple, fpsimple, + *, mtjmpr, mfjmpr, *")]) + +;; LWZ LFIWZX STW STFIWX MTVSRWZ MFVSRWZ +;; FMR MR MT%0 MF%1 NOP +(define_insn "movsd_hardfloat" + [(set (match_operand:SD 0 "nonimmediate_operand" + "=!r, wz, m, Z, ?wh, ?r, + f, !r, *c*l, !r, *h") + (match_operand:SD 1 "input_operand" + "m, Z, r, wx, r, wh, + f, r, r, *h, 0"))] + "(register_operand (operands[0], SDmode) + || register_operand (operands[1], SDmode)) + && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT" + "@ + lwz%U1%X1 %0,%1 + lfiwzx %0,%y1 + stw%U0%X0 %1,%0 + stfiwx %1,%y0 + mtvsrwz %x0,%1 + mfvsrwz %0,%x1 + fmr %0,%1 + mr %0,%1 + mt%0 %1 + mf%1 %0 + nop" + [(set_attr "type" + "load, fpload, store, fpstore, mffgpr, mftgpr, + fpsimple, *, mtjmpr, mfjmpr, *")]) + +(define_insn "*mov<mode>_softfloat" + [(set (match_operand:FMOVE32 0 "nonimmediate_operand" "=r,cl,r,r,m,r,r,r,r,*h") + (match_operand:FMOVE32 1 "input_operand" "r,r,h,m,r,I,L,G,Fn,0"))] + "(gpc_reg_operand (operands[0], <MODE>mode) + || gpc_reg_operand (operands[1], <MODE>mode)) + && (TARGET_SOFT_FLOAT || !TARGET_FPRS)" + "@ + mr %0,%1 + mt%0 %1 + mf%1 %0 + lwz%U1%X1 %0,%1 + stw%U0%X0 %1,%0 + li %0,%1 + lis %0,%v1 + # + # + nop" + [(set_attr "type" "*,mtjmpr,mfjmpr,load,store,*,*,*,*,*") + (set_attr "length" "4,4,4,4,4,4,4,4,8,4")]) + +;; Like movsf, but adjust a SI value to be used in a SF context, i.e. +;; (set (reg:SF ...) (subreg:SF (reg:SI ...) 0)) +;; +;; Because SF values are actually stored as DF values within the vector +;; registers, we need to convert the value to the vector SF format when +;; we need to use the bits in a union or similar cases. We only need +;; to do this transformation when the value is a vector register. Loads, +;; stores, and transfers within GPRs are assumed to be safe. +;; +;; This is a more general case of reload_vsx_from_gprsf. That insn must have +;; no alternatives, because the call is created as part of secondary_reload, +;; and operand #2's register class is used to allocate the temporary register. +;; This function is called before reload, and it creates the temporary as +;; needed. + +;; LWZ LFS LXSSP LXSSPX STW STFIWX +;; STXSIWX GPR->VSX VSX->GPR GPR->GPR +(define_insn_and_split "movsf_from_si" + [(set (match_operand:SF 0 "rs6000_nonimmediate_operand" + "=!r, f, wb, wu, m, Z, + Z, wy, ?r, !r") + + (unspec:SF [(match_operand:SI 1 "input_operand" + "m, m, wY, Z, r, f, + wu, r, wy, r")] + UNSPEC_SF_FROM_SI)) + + (clobber (match_scratch:DI 2 + "=X, X, X, X, X, X, + X, r, X, X"))] + + "TARGET_NO_SF_SUBREG + && (register_operand (operands[0], SFmode) + || register_operand (operands[1], SImode))" + "@ + lwz%U1%X1 %0,%1 + lfs%U1%X1 %0,%1 + lxssp %0,%1 + lxsspx %x0,%y1 + stw%U0%X0 %1,%0 + stfiwx %1,%y0 + stxsiwx %x1,%y0 + # + mfvsrwz %0,%x1 + mr %0,%1" + + "&& reload_completed + && vsx_reg_sfsubreg_ok (operands[0], SFmode) + && int_reg_operand_not_pseudo (operands[1], SImode)" + [(const_int 0)] +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + rtx op2 = operands[2]; + rtx op1_di = gen_rtx_REG (DImode, REGNO (op1)); + + /* Move SF value to upper 32-bits for xscvspdpn. */ + emit_insn (gen_ashldi3 (op2, op1_di, GEN_INT (32))); + emit_insn (gen_p8_mtvsrd_sf (op0, op2)); + emit_insn (gen_vsx_xscvspdpn_directmove (op0, op0)); + DONE; +} + [(set_attr "length" + "4, 4, 4, 4, 4, 4, + 4, 12, 4, 4") + (set_attr "type" + "load, fpload, fpload, fpload, store, fpstore, + fpstore, vecfloat, mffgpr, *")]) + + +;; Move 64-bit binary/decimal floating point +(define_expand "mov<mode>" + [(set (match_operand:FMOVE64 0 "nonimmediate_operand" "") + (match_operand:FMOVE64 1 "any_operand" ""))] + "" + "{ rs6000_emit_move (operands[0], operands[1], <MODE>mode); DONE; }") + +(define_split + [(set (match_operand:FMOVE64 0 "gpc_reg_operand" "") + (match_operand:FMOVE64 1 "const_int_operand" ""))] + "! TARGET_POWERPC64 && reload_completed + && ((GET_CODE (operands[0]) == REG && REGNO (operands[0]) <= 31) + || (GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == REG + && REGNO (SUBREG_REG (operands[0])) <= 31))" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 1))] + " +{ + int endian = (WORDS_BIG_ENDIAN == 0); + HOST_WIDE_INT value = INTVAL (operands[1]); + + operands[2] = operand_subword (operands[0], endian, 0, <MODE>mode); + operands[3] = operand_subword (operands[0], 1 - endian, 0, <MODE>mode); + operands[4] = GEN_INT (value >> 32); + operands[1] = GEN_INT (((value & 0xffffffff) ^ 0x80000000) - 0x80000000); +}") + +(define_split + [(set (match_operand:FMOVE64 0 "gpc_reg_operand" "") + (match_operand:FMOVE64 1 "const_double_operand" ""))] + "! TARGET_POWERPC64 && reload_completed + && ((GET_CODE (operands[0]) == REG && REGNO (operands[0]) <= 31) + || (GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == REG + && REGNO (SUBREG_REG (operands[0])) <= 31))" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] + " +{ + int endian = (WORDS_BIG_ENDIAN == 0); + long l[2]; + + <real_value_to_target> (*CONST_DOUBLE_REAL_VALUE (operands[1]), l); + + operands[2] = operand_subword (operands[0], endian, 0, <MODE>mode); + operands[3] = operand_subword (operands[0], 1 - endian, 0, <MODE>mode); + operands[4] = gen_int_mode (l[endian], SImode); + operands[5] = gen_int_mode (l[1 - endian], SImode); +}") + +(define_split + [(set (match_operand:FMOVE64 0 "gpc_reg_operand" "") + (match_operand:FMOVE64 1 "const_double_operand" ""))] + "TARGET_POWERPC64 && reload_completed + && ((GET_CODE (operands[0]) == REG && REGNO (operands[0]) <= 31) + || (GET_CODE (operands[0]) == SUBREG + && GET_CODE (SUBREG_REG (operands[0])) == REG + && REGNO (SUBREG_REG (operands[0])) <= 31))" + [(set (match_dup 2) (match_dup 3))] + " +{ + int endian = (WORDS_BIG_ENDIAN == 0); + long l[2]; + HOST_WIDE_INT val; + + <real_value_to_target> (*CONST_DOUBLE_REAL_VALUE (operands[1]), l); + + operands[2] = gen_lowpart (DImode, operands[0]); + /* HIGHPART is lower memory address when WORDS_BIG_ENDIAN. */ + val = ((HOST_WIDE_INT)(unsigned long)l[endian] << 32 + | ((HOST_WIDE_INT)(unsigned long)l[1 - endian])); + + operands[3] = gen_int_mode (val, DImode); +}") + +;; Don't have reload use general registers to load a constant. It is +;; less efficient than loading the constant into an FP register, since +;; it will probably be used there. + +;; The move constraints are ordered to prefer floating point registers before +;; general purpose registers to avoid doing a store and a load to get the value +;; into a floating point register when it is needed for a floating point +;; operation. Prefer traditional floating point registers over VSX registers, +;; since the D-form version of the memory instructions does not need a GPR for +;; reloading. ISA 3.0 (power9) adds D-form addressing for scalars to Altivec +;; registers. + +;; If we have FPR registers, rs6000_emit_move has moved all constants to memory, +;; except for 0.0 which can be created on VSX with an xor instruction. + +(define_insn "*mov<mode>_hardfloat32" + [(set (match_operand:FMOVE64 0 "nonimmediate_operand" "=m,d,d,<f64_av>,Z,<f64_p9>,wY,<f64_vsx>,<f64_vsx>,!r,Y,r,!r") + (match_operand:FMOVE64 1 "input_operand" "d,m,d,Z,<f64_av>,wY,<f64_p9>,<f64_vsx>,<zero_fp>,<zero_fp>,r,Y,r"))] + "! TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && (gpc_reg_operand (operands[0], <MODE>mode) + || gpc_reg_operand (operands[1], <MODE>mode))" + "@ + stfd%U0%X0 %1,%0 + lfd%U1%X1 %0,%1 + fmr %0,%1 + lxsd%U1x %x0,%y1 + stxsd%U0x %x1,%y0 + lxsd %0,%1 + stxsd %1,%0 + xxlor %x0,%x1,%x1 + xxlxor %x0,%x0,%x0 + # + # + # + #" + [(set_attr "type" "fpstore,fpload,fpsimple,fpload,fpstore,fpload,fpstore,veclogical,veclogical,two,store,load,two") + (set_attr "size" "64") + (set_attr "length" "4,4,4,4,4,4,4,4,4,8,8,8,8")]) + +(define_insn "*mov<mode>_softfloat32" + [(set (match_operand:FMOVE64 0 "nonimmediate_operand" "=Y,r,r,r,r,r") + (match_operand:FMOVE64 1 "input_operand" "r,Y,r,G,H,F"))] + "! TARGET_POWERPC64 + && ((TARGET_FPRS && TARGET_SINGLE_FLOAT) + || TARGET_SOFT_FLOAT || TARGET_E500_SINGLE + || (<MODE>mode == DDmode && TARGET_E500_DOUBLE)) + && (gpc_reg_operand (operands[0], <MODE>mode) + || gpc_reg_operand (operands[1], <MODE>mode))" + "#" + [(set_attr "type" "store,load,two,*,*,*") + (set_attr "length" "8,8,8,8,12,16")]) + +; ld/std require word-aligned displacements -> 'Y' constraint. +; List Y->r and r->Y before r->r for reload. +(define_insn "*mov<mode>_hardfloat64" + [(set (match_operand:FMOVE64 0 "nonimmediate_operand" "=m,d,d,<f64_p9>,wY,<f64_av>,Z,<f64_vsx>,<f64_vsx>,!r,Y,r,!r,*c*l,!r,*h,r,wg,r,<f64_dm>") + (match_operand:FMOVE64 1 "input_operand" "d,m,d,wY,<f64_p9>,Z,<f64_av>,<f64_vsx>,<zero_fp>,<zero_fp>,r,Y,r,r,h,0,wg,r,<f64_dm>,r"))] + "TARGET_POWERPC64 && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && (gpc_reg_operand (operands[0], <MODE>mode) + || gpc_reg_operand (operands[1], <MODE>mode))" + "@ + stfd%U0%X0 %1,%0 + lfd%U1%X1 %0,%1 + fmr %0,%1 + lxsd %0,%1 + stxsd %1,%0 + lxsd%U1x %x0,%y1 + stxsd%U0x %x1,%y0 + xxlor %x0,%x1,%x1 + xxlxor %x0,%x0,%x0 + li %0,0 + std%U0%X0 %1,%0 + ld%U1%X1 %0,%1 + mr %0,%1 + mt%0 %1 + mf%1 %0 + nop + mftgpr %0,%1 + mffgpr %0,%1 + mfvsrd %0,%x1 + mtvsrd %x0,%1" + [(set_attr "type" "fpstore,fpload,fpsimple,fpload,fpstore,fpload,fpstore,veclogical,veclogical,integer,store,load,*,mtjmpr,mfjmpr,*,mftgpr,mffgpr,mftgpr,mffgpr") + (set_attr "size" "64") + (set_attr "length" "4")]) + +(define_insn "*mov<mode>_softfloat64" + [(set (match_operand:FMOVE64 0 "nonimmediate_operand" "=Y,r,r,cl,r,r,r,r,*h") + (match_operand:FMOVE64 1 "input_operand" "r,Y,r,r,h,G,H,F,0"))] + "TARGET_POWERPC64 && (TARGET_SOFT_FLOAT || !TARGET_FPRS) + && (gpc_reg_operand (operands[0], <MODE>mode) + || gpc_reg_operand (operands[1], <MODE>mode))" + "@ + std%U0%X0 %1,%0 + ld%U1%X1 %0,%1 + mr %0,%1 + mt%0 %1 + mf%1 %0 + # + # + # + nop" + [(set_attr "type" "store,load,*,mtjmpr,mfjmpr,*,*,*,*") + (set_attr "length" "4,4,4,4,4,8,12,16,4")]) + +(define_expand "mov<mode>" + [(set (match_operand:FMOVE128 0 "general_operand" "") + (match_operand:FMOVE128 1 "any_operand" ""))] + "" + "{ rs6000_emit_move (operands[0], operands[1], <MODE>mode); DONE; }") + +;; It's important to list Y->r and r->Y before r->r because otherwise +;; reload, given m->r, will try to pick r->r and reload it, which +;; doesn't make progress. + +;; We can't split little endian direct moves of TDmode, because the words are +;; not swapped like they are for TImode or TFmode. Subregs therefore are +;; problematical. Don't allow direct move for this case. + +(define_insn_and_split "*mov<mode>_64bit_dm" + [(set (match_operand:FMOVE128_FPR 0 "nonimmediate_operand" "=m,d,d,d,Y,r,r,r,wh") + (match_operand:FMOVE128_FPR 1 "input_operand" "d,m,d,<zero_fp>,r,<zero_fp>Y,r,wh,r"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_POWERPC64 + && FLOAT128_2REG_P (<MODE>mode) + && (<MODE>mode != TDmode || WORDS_BIG_ENDIAN) + && (gpc_reg_operand (operands[0], <MODE>mode) + || gpc_reg_operand (operands[1], <MODE>mode))" + "#" + "&& reload_completed" + [(pc)] +{ rs6000_split_multireg_move (operands[0], operands[1]); DONE; } + [(set_attr "length" "8,8,8,8,12,12,8,8,8")]) + +(define_insn_and_split "*movtd_64bit_nodm" + [(set (match_operand:TD 0 "nonimmediate_operand" "=m,d,d,Y,r,r") + (match_operand:TD 1 "input_operand" "d,m,d,r,Y,r"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_POWERPC64 && !WORDS_BIG_ENDIAN + && (gpc_reg_operand (operands[0], TDmode) + || gpc_reg_operand (operands[1], TDmode))" + "#" + "&& reload_completed" + [(pc)] +{ rs6000_split_multireg_move (operands[0], operands[1]); DONE; } + [(set_attr "length" "8,8,8,12,12,8")]) + +(define_insn_and_split "*mov<mode>_32bit" + [(set (match_operand:FMOVE128_FPR 0 "nonimmediate_operand" "=m,d,d,d,Y,r,r") + (match_operand:FMOVE128_FPR 1 "input_operand" "d,m,d,<zero_fp>,r,<zero_fp>Y,r"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && !TARGET_POWERPC64 + && (FLOAT128_2REG_P (<MODE>mode) + || int_reg_operand_not_pseudo (operands[0], <MODE>mode) + || int_reg_operand_not_pseudo (operands[1], <MODE>mode)) + && (gpc_reg_operand (operands[0], <MODE>mode) + || gpc_reg_operand (operands[1], <MODE>mode))" + "#" + "&& reload_completed" + [(pc)] +{ rs6000_split_multireg_move (operands[0], operands[1]); DONE; } + [(set_attr "length" "8,8,8,8,20,20,16")]) + +(define_insn_and_split "*mov<mode>_softfloat" + [(set (match_operand:FMOVE128 0 "rs6000_nonimmediate_operand" "=Y,r,r") + (match_operand:FMOVE128 1 "input_operand" "r,YGHF,r"))] + "(TARGET_SOFT_FLOAT || !TARGET_FPRS) + && (gpc_reg_operand (operands[0], <MODE>mode) + || gpc_reg_operand (operands[1], <MODE>mode))" + "#" + "&& reload_completed" + [(pc)] +{ rs6000_split_multireg_move (operands[0], operands[1]); DONE; } + [(set_attr "length" "20,20,16")]) + +(define_expand "extenddf<mode>2" + [(set (match_operand:FLOAT128 0 "gpc_reg_operand" "") + (float_extend:FLOAT128 (match_operand:DF 1 "gpc_reg_operand" "")))] + "TARGET_HARD_FLOAT && (TARGET_FPRS || TARGET_E500_DOUBLE) + && TARGET_LONG_DOUBLE_128" +{ + if (FLOAT128_IEEE_P (<MODE>mode)) + rs6000_expand_float128_convert (operands[0], operands[1], false); + else if (TARGET_E500_DOUBLE) + { + gcc_assert (<MODE>mode == TFmode); + emit_insn (gen_spe_extenddftf2 (operands[0], operands[1])); + } + else if (TARGET_VSX) + { + if (<MODE>mode == TFmode) + emit_insn (gen_extenddftf2_vsx (operands[0], operands[1])); + else if (<MODE>mode == IFmode) + emit_insn (gen_extenddfif2_vsx (operands[0], operands[1])); + else + gcc_unreachable (); + } + else + { + rtx zero = gen_reg_rtx (DFmode); + rs6000_emit_move (zero, CONST0_RTX (DFmode), DFmode); + + if (<MODE>mode == TFmode) + emit_insn (gen_extenddftf2_fprs (operands[0], operands[1], zero)); + else if (<MODE>mode == IFmode) + emit_insn (gen_extenddfif2_fprs (operands[0], operands[1], zero)); + else + gcc_unreachable (); + } + DONE; +}) + +;; Allow memory operands for the source to be created by the combiner. +(define_insn_and_split "extenddf<mode>2_fprs" + [(set (match_operand:IBM128 0 "gpc_reg_operand" "=d,d,&d") + (float_extend:IBM128 + (match_operand:DF 1 "nonimmediate_operand" "d,m,d"))) + (use (match_operand:DF 2 "nonimmediate_operand" "m,m,d"))] + "!TARGET_VSX && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && TARGET_LONG_DOUBLE_128 && FLOAT128_IBM_P (<MODE>mode)" + "#" + "&& reload_completed" + [(set (match_dup 3) (match_dup 1)) + (set (match_dup 4) (match_dup 2))] +{ + const int lo_word = LONG_DOUBLE_LARGE_FIRST ? GET_MODE_SIZE (DFmode) : 0; + const int hi_word = LONG_DOUBLE_LARGE_FIRST ? 0 : GET_MODE_SIZE (DFmode); + + operands[3] = simplify_gen_subreg (DFmode, operands[0], <MODE>mode, hi_word); + operands[4] = simplify_gen_subreg (DFmode, operands[0], <MODE>mode, lo_word); +}) + +(define_insn_and_split "extenddf<mode>2_vsx" + [(set (match_operand:IBM128 0 "gpc_reg_operand" "=d,d") + (float_extend:IBM128 + (match_operand:DF 1 "nonimmediate_operand" "ws,m")))] + "TARGET_LONG_DOUBLE_128 && TARGET_VSX && FLOAT128_IBM_P (<MODE>mode)" + "#" + "&& reload_completed" + [(set (match_dup 2) (match_dup 1)) + (set (match_dup 3) (match_dup 4))] +{ + const int lo_word = LONG_DOUBLE_LARGE_FIRST ? GET_MODE_SIZE (DFmode) : 0; + const int hi_word = LONG_DOUBLE_LARGE_FIRST ? 0 : GET_MODE_SIZE (DFmode); + + operands[2] = simplify_gen_subreg (DFmode, operands[0], <MODE>mode, hi_word); + operands[3] = simplify_gen_subreg (DFmode, operands[0], <MODE>mode, lo_word); + operands[4] = CONST0_RTX (DFmode); +}) + +(define_expand "extendsf<mode>2" + [(set (match_operand:FLOAT128 0 "gpc_reg_operand" "") + (float_extend:FLOAT128 (match_operand:SF 1 "gpc_reg_operand" "")))] + "TARGET_HARD_FLOAT + && (TARGET_FPRS || TARGET_E500_DOUBLE) + && TARGET_LONG_DOUBLE_128" +{ + if (FLOAT128_IEEE_P (<MODE>mode)) + rs6000_expand_float128_convert (operands[0], operands[1], false); + else + { + rtx tmp = gen_reg_rtx (DFmode); + emit_insn (gen_extendsfdf2 (tmp, operands[1])); + emit_insn (gen_extenddf<mode>2 (operands[0], tmp)); + } + DONE; +}) + +(define_expand "trunc<mode>df2" + [(set (match_operand:DF 0 "gpc_reg_operand" "") + (float_truncate:DF (match_operand:FLOAT128 1 "gpc_reg_operand" "")))] + "TARGET_HARD_FLOAT + && (TARGET_FPRS || TARGET_E500_DOUBLE) + && TARGET_LONG_DOUBLE_128" +{ + if (FLOAT128_IEEE_P (<MODE>mode)) + { + rs6000_expand_float128_convert (operands[0], operands[1], false); + DONE; + } +}) + +(define_insn_and_split "trunc<mode>df2_internal1" + [(set (match_operand:DF 0 "gpc_reg_operand" "=d,?d") + (float_truncate:DF + (match_operand:IBM128 1 "gpc_reg_operand" "0,d")))] + "FLOAT128_IBM_P (<MODE>mode) && !TARGET_XL_COMPAT + && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128" + "@ + # + fmr %0,%1" + "&& reload_completed && REGNO (operands[0]) == REGNO (operands[1])" + [(const_int 0)] +{ + emit_note (NOTE_INSN_DELETED); + DONE; +} + [(set_attr "type" "fpsimple")]) + +(define_insn "trunc<mode>df2_internal2" + [(set (match_operand:DF 0 "gpc_reg_operand" "=d") + (float_truncate:DF (match_operand:IBM128 1 "gpc_reg_operand" "d")))] + "FLOAT128_IBM_P (<MODE>mode) && TARGET_XL_COMPAT && TARGET_HARD_FLOAT + && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LONG_DOUBLE_128" + "fadd %0,%1,%L1" + [(set_attr "type" "fp") + (set_attr "fp_type" "fp_addsub_d")]) + +(define_expand "trunc<mode>sf2" + [(set (match_operand:SF 0 "gpc_reg_operand" "") + (float_truncate:SF (match_operand:FLOAT128 1 "gpc_reg_operand" "")))] + "TARGET_HARD_FLOAT + && (TARGET_FPRS || TARGET_E500_DOUBLE) + && TARGET_LONG_DOUBLE_128" +{ + if (FLOAT128_IEEE_P (<MODE>mode)) + rs6000_expand_float128_convert (operands[0], operands[1], false); + else if (TARGET_E500_DOUBLE) + { + gcc_assert (<MODE>mode == TFmode); + emit_insn (gen_spe_trunctfsf2 (operands[0], operands[1])); + } + else if (<MODE>mode == TFmode) + emit_insn (gen_trunctfsf2_fprs (operands[0], operands[1])); + else if (<MODE>mode == IFmode) + emit_insn (gen_truncifsf2_fprs (operands[0], operands[1])); + else + gcc_unreachable (); + DONE; +}) + +(define_insn_and_split "trunc<mode>sf2_fprs" + [(set (match_operand:SF 0 "gpc_reg_operand" "=f") + (float_truncate:SF (match_operand:IBM128 1 "gpc_reg_operand" "d"))) + (clobber (match_scratch:DF 2 "=d"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT + && TARGET_LONG_DOUBLE_128 && FLOAT128_IBM_P (<MODE>mode)" + "#" + "&& reload_completed" + [(set (match_dup 2) + (float_truncate:DF (match_dup 1))) + (set (match_dup 0) + (float_truncate:SF (match_dup 2)))] + "") + +(define_expand "floatsi<mode>2" + [(parallel [(set (match_operand:FLOAT128 0 "gpc_reg_operand") + (float:FLOAT128 (match_operand:SI 1 "gpc_reg_operand"))) + (clobber (match_scratch:DI 2))])] + "TARGET_HARD_FLOAT + && (TARGET_FPRS || TARGET_E500_DOUBLE) + && TARGET_LONG_DOUBLE_128" +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + + if (TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)) + ; + else if (FLOAT128_IEEE_P (<MODE>mode)) + { + rs6000_expand_float128_convert (op0, op1, false); + DONE; + } + else + { + rtx tmp = gen_reg_rtx (DFmode); + expand_float (tmp, op1, false); + if (<MODE>mode == TFmode) + emit_insn (gen_extenddftf2 (op0, tmp)); + else if (<MODE>mode == IFmode) + emit_insn (gen_extenddfif2 (op0, tmp)); + else + gcc_unreachable (); + DONE; + } +}) + +; fadd, but rounding towards zero. +; This is probably not the optimal code sequence. +(define_insn "fix_trunc_helper<mode>" + [(set (match_operand:DF 0 "gpc_reg_operand" "=d") + (unspec:DF [(match_operand:IBM128 1 "gpc_reg_operand" "d")] + UNSPEC_FIX_TRUNC_TF)) + (clobber (match_operand:DF 2 "gpc_reg_operand" "=&d"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && FLOAT128_IBM_P (<MODE>mode)" + "mffs %2\n\tmtfsb1 31\n\tmtfsb0 30\n\tfadd %0,%1,%L1\n\tmtfsf 1,%2" + [(set_attr "type" "fp") + (set_attr "length" "20")]) + +(define_expand "fix_trunc<mode>si2" + [(set (match_operand:SI 0 "gpc_reg_operand" "") + (fix:SI (match_operand:FLOAT128 1 "gpc_reg_operand" "")))] + "TARGET_HARD_FLOAT + && (TARGET_FPRS || TARGET_E500_DOUBLE) && TARGET_LONG_DOUBLE_128" +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + + if (TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)) + ; + else + { + if (FLOAT128_IEEE_P (<MODE>mode)) + rs6000_expand_float128_convert (op0, op1, false); + else if (TARGET_E500_DOUBLE && <MODE>mode == TFmode) + emit_insn (gen_spe_fix_trunctfsi2 (op0, op1)); + else if (<MODE>mode == TFmode) + emit_insn (gen_fix_trunctfsi2_fprs (op0, op1)); + else if (<MODE>mode == IFmode) + emit_insn (gen_fix_truncifsi2_fprs (op0, op1)); + else + gcc_unreachable (); + DONE; + } +}) + +(define_expand "fix_trunc<mode>si2_fprs" + [(parallel [(set (match_operand:SI 0 "gpc_reg_operand" "") + (fix:SI (match_operand:IBM128 1 "gpc_reg_operand" ""))) + (clobber (match_dup 2)) + (clobber (match_dup 3)) + (clobber (match_dup 4)) + (clobber (match_dup 5))])] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128" +{ + operands[2] = gen_reg_rtx (DFmode); + operands[3] = gen_reg_rtx (DFmode); + operands[4] = gen_reg_rtx (DImode); + operands[5] = assign_stack_temp (DImode, GET_MODE_SIZE (DImode)); +}) + +(define_insn_and_split "*fix_trunc<mode>si2_internal" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (fix:SI (match_operand:IBM128 1 "gpc_reg_operand" "d"))) + (clobber (match_operand:DF 2 "gpc_reg_operand" "=d")) + (clobber (match_operand:DF 3 "gpc_reg_operand" "=&d")) + (clobber (match_operand:DI 4 "gpc_reg_operand" "=d")) + (clobber (match_operand:DI 5 "offsettable_mem_operand" "=o"))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128" + "#" + "" + [(pc)] +{ + rtx lowword; + emit_insn (gen_fix_trunc_helper<mode> (operands[2], operands[1], + operands[3])); + + gcc_assert (MEM_P (operands[5])); + lowword = adjust_address (operands[5], SImode, WORDS_BIG_ENDIAN ? 4 : 0); + + emit_insn (gen_fctiwz_df (operands[4], operands[2])); + emit_move_insn (operands[5], operands[4]); + emit_move_insn (operands[0], lowword); + DONE; +}) + +(define_expand "fix_trunc<mode>di2" + [(set (match_operand:DI 0 "gpc_reg_operand" "") + (fix:DI (match_operand:IEEE128 1 "gpc_reg_operand" "")))] + "TARGET_FLOAT128_TYPE" +{ + if (!TARGET_FLOAT128_HW) + { + rs6000_expand_float128_convert (operands[0], operands[1], false); + DONE; + } +}) + +(define_expand "fixuns_trunc<IEEE128:mode><SDI:mode>2" + [(set (match_operand:SDI 0 "gpc_reg_operand" "") + (unsigned_fix:SDI (match_operand:IEEE128 1 "gpc_reg_operand" "")))] + "TARGET_FLOAT128_TYPE" +{ + rs6000_expand_float128_convert (operands[0], operands[1], true); + DONE; +}) + +(define_expand "floatdi<mode>2" + [(set (match_operand:IEEE128 0 "gpc_reg_operand" "") + (float:IEEE128 (match_operand:DI 1 "gpc_reg_operand" "")))] + "TARGET_FLOAT128_TYPE" +{ + if (!TARGET_FLOAT128_HW) + { + rs6000_expand_float128_convert (operands[0], operands[1], false); + DONE; + } +}) + +(define_expand "floatunsdi<IEEE128:mode>2" + [(set (match_operand:IEEE128 0 "gpc_reg_operand" "") + (unsigned_float:IEEE128 (match_operand:DI 1 "gpc_reg_operand" "")))] + "TARGET_FLOAT128_TYPE" +{ + if (!TARGET_FLOAT128_HW) + { + rs6000_expand_float128_convert (operands[0], operands[1], true); + DONE; + } +}) + +(define_expand "floatuns<IEEE128:mode>2" + [(set (match_operand:IEEE128 0 "gpc_reg_operand" "") + (unsigned_float:IEEE128 (match_operand:SI 1 "gpc_reg_operand" "")))] + "TARGET_FLOAT128_TYPE" +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + + if (TARGET_FLOAT128_HW) + emit_insn (gen_floatuns_<IEEE128:mode>si2_hw (op0, op1)); + else + rs6000_expand_float128_convert (op0, op1, true); + DONE; +}) + +(define_expand "neg<mode>2" + [(set (match_operand:FLOAT128 0 "gpc_reg_operand" "") + (neg:FLOAT128 (match_operand:FLOAT128 1 "gpc_reg_operand" "")))] + "FLOAT128_IEEE_P (<MODE>mode) + || (FLOAT128_IBM_P (<MODE>mode) + && TARGET_HARD_FLOAT + && (TARGET_FPRS || TARGET_E500_DOUBLE))" + " +{ + if (FLOAT128_IEEE_P (<MODE>mode)) + { + if (TARGET_FLOAT128_HW) + { + if (<MODE>mode == TFmode) + emit_insn (gen_negtf2_hw (operands[0], operands[1])); + else if (<MODE>mode == KFmode) + emit_insn (gen_negkf2_hw (operands[0], operands[1])); + else + gcc_unreachable (); + } + else if (TARGET_FLOAT128_TYPE) + { + if (<MODE>mode == TFmode) + emit_insn (gen_ieee_128bit_vsx_negtf2 (operands[0], operands[1])); + else if (<MODE>mode == KFmode) + emit_insn (gen_ieee_128bit_vsx_negkf2 (operands[0], operands[1])); + else + gcc_unreachable (); + } + else + { + rtx libfunc = optab_libfunc (neg_optab, <MODE>mode); + rtx target = emit_library_call_value (libfunc, operands[0], LCT_CONST, + <MODE>mode, 1, + operands[1], <MODE>mode); + + if (target && !rtx_equal_p (target, operands[0])) + emit_move_insn (operands[0], target); + } + DONE; + } +}") + +(define_insn "neg<mode>2_internal" + [(set (match_operand:IBM128 0 "gpc_reg_operand" "=d") + (neg:IBM128 (match_operand:IBM128 1 "gpc_reg_operand" "d")))] + "TARGET_HARD_FLOAT && TARGET_FPRS && FLOAT128_IBM_P (TFmode)" + "* +{ + if (REGNO (operands[0]) == REGNO (operands[1]) + 1) + return \"fneg %L0,%L1\;fneg %0,%1\"; + else + return \"fneg %0,%1\;fneg %L0,%L1\"; +}" + [(set_attr "type" "fpsimple") + (set_attr "length" "8")]) + +(define_expand "abs<mode>2" + [(set (match_operand:FLOAT128 0 "gpc_reg_operand" "") + (abs:FLOAT128 (match_operand:FLOAT128 1 "gpc_reg_operand" "")))] + "FLOAT128_IEEE_P (<MODE>mode) + || (FLOAT128_IBM_P (<MODE>mode) + && TARGET_HARD_FLOAT + && (TARGET_FPRS || TARGET_E500_DOUBLE))" + " +{ + rtx label; + + if (FLOAT128_IEEE_P (<MODE>mode)) + { + if (TARGET_FLOAT128_HW) + { + if (<MODE>mode == TFmode) + emit_insn (gen_abstf2_hw (operands[0], operands[1])); + else if (<MODE>mode == KFmode) + emit_insn (gen_abskf2_hw (operands[0], operands[1])); + else + FAIL; + DONE; + } + else if (TARGET_FLOAT128_TYPE) + { + if (<MODE>mode == TFmode) + emit_insn (gen_ieee_128bit_vsx_abstf2 (operands[0], operands[1])); + else if (<MODE>mode == KFmode) + emit_insn (gen_ieee_128bit_vsx_abskf2 (operands[0], operands[1])); + else + FAIL; + DONE; + } + else + FAIL; + } + + label = gen_label_rtx (); + if (TARGET_E500_DOUBLE && <MODE>mode == TFmode) + { + if (flag_finite_math_only && !flag_trapping_math) + emit_insn (gen_spe_abstf2_tst (operands[0], operands[1], label)); + else + emit_insn (gen_spe_abstf2_cmp (operands[0], operands[1], label)); + } + else if (<MODE>mode == TFmode) + emit_insn (gen_abstf2_internal (operands[0], operands[1], label)); + else if (<MODE>mode == TFmode) + emit_insn (gen_absif2_internal (operands[0], operands[1], label)); + else + FAIL; + emit_label (label); + DONE; +}") + +(define_expand "abs<mode>2_internal" + [(set (match_operand:IBM128 0 "gpc_reg_operand" "") + (match_operand:IBM128 1 "gpc_reg_operand" "")) + (set (match_dup 3) (match_dup 5)) + (set (match_dup 5) (abs:DF (match_dup 5))) + (set (match_dup 4) (compare:CCFP (match_dup 3) (match_dup 5))) + (set (pc) (if_then_else (eq (match_dup 4) (const_int 0)) + (label_ref (match_operand 2 "" "")) + (pc))) + (set (match_dup 6) (neg:DF (match_dup 6)))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT + && TARGET_LONG_DOUBLE_128" + " +{ + const int hi_word = LONG_DOUBLE_LARGE_FIRST ? 0 : GET_MODE_SIZE (DFmode); + const int lo_word = LONG_DOUBLE_LARGE_FIRST ? GET_MODE_SIZE (DFmode) : 0; + operands[3] = gen_reg_rtx (DFmode); + operands[4] = gen_reg_rtx (CCFPmode); + operands[5] = simplify_gen_subreg (DFmode, operands[0], <MODE>mode, hi_word); + operands[6] = simplify_gen_subreg (DFmode, operands[0], <MODE>mode, lo_word); +}") + + +;; Generate IEEE 128-bit -0.0 (0x80000000000000000000000000000000) in a vector +;; register + +(define_expand "ieee_128bit_negative_zero" + [(set (match_operand:V16QI 0 "register_operand" "") (match_dup 1))] + "TARGET_FLOAT128_TYPE" +{ + rtvec v = rtvec_alloc (16); + int i, high; + + for (i = 0; i < 16; i++) + RTVEC_ELT (v, i) = const0_rtx; + + high = (BYTES_BIG_ENDIAN) ? 0 : 15; + RTVEC_ELT (v, high) = GEN_INT (0x80); + + rs6000_expand_vector_init (operands[0], gen_rtx_PARALLEL (V16QImode, v)); + DONE; +}) + +;; IEEE 128-bit negate + +;; We have 2 insns here for negate and absolute value. The first uses +;; match_scratch so that phases like combine can recognize neg/abs as generic +;; insns, and second insn after the first split pass loads up the bit to +;; twiddle the sign bit. Later GCSE passes can then combine multiple uses of +;; neg/abs to create the constant just once. + +(define_insn_and_split "ieee_128bit_vsx_neg<mode>2" + [(set (match_operand:IEEE128 0 "register_operand" "=wa") + (neg:IEEE128 (match_operand:IEEE128 1 "register_operand" "wa"))) + (clobber (match_scratch:V16QI 2 "=v"))] + "TARGET_FLOAT128_TYPE && !TARGET_FLOAT128_HW" + "#" + "&& 1" + [(parallel [(set (match_dup 0) + (neg:IEEE128 (match_dup 1))) + (use (match_dup 2))])] +{ + if (GET_CODE (operands[2]) == SCRATCH) + operands[2] = gen_reg_rtx (V16QImode); + + operands[3] = gen_reg_rtx (V16QImode); + emit_insn (gen_ieee_128bit_negative_zero (operands[2])); +} + [(set_attr "length" "8") + (set_attr "type" "vecsimple")]) + +(define_insn "*ieee_128bit_vsx_neg<mode>2_internal" + [(set (match_operand:IEEE128 0 "register_operand" "=wa") + (neg:IEEE128 (match_operand:IEEE128 1 "register_operand" "wa"))) + (use (match_operand:V16QI 2 "register_operand" "v"))] + "TARGET_FLOAT128_TYPE && !TARGET_FLOAT128_HW" + "xxlxor %x0,%x1,%x2" + [(set_attr "type" "veclogical")]) + +;; IEEE 128-bit absolute value +(define_insn_and_split "ieee_128bit_vsx_abs<mode>2" + [(set (match_operand:IEEE128 0 "register_operand" "=wa") + (abs:IEEE128 (match_operand:IEEE128 1 "register_operand" "wa"))) + (clobber (match_scratch:V16QI 2 "=v"))] + "TARGET_FLOAT128_TYPE && !TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "#" + "&& 1" + [(parallel [(set (match_dup 0) + (abs:IEEE128 (match_dup 1))) + (use (match_dup 2))])] +{ + if (GET_CODE (operands[2]) == SCRATCH) + operands[2] = gen_reg_rtx (V16QImode); + + operands[3] = gen_reg_rtx (V16QImode); + emit_insn (gen_ieee_128bit_negative_zero (operands[2])); +} + [(set_attr "length" "8") + (set_attr "type" "vecsimple")]) + +(define_insn "*ieee_128bit_vsx_abs<mode>2_internal" + [(set (match_operand:IEEE128 0 "register_operand" "=wa") + (abs:IEEE128 (match_operand:IEEE128 1 "register_operand" "wa"))) + (use (match_operand:V16QI 2 "register_operand" "v"))] + "TARGET_FLOAT128_TYPE && !TARGET_FLOAT128_HW" + "xxlandc %x0,%x1,%x2" + [(set_attr "type" "veclogical")]) + +;; IEEE 128-bit negative absolute value +(define_insn_and_split "*ieee_128bit_vsx_nabs<mode>2" + [(set (match_operand:IEEE128 0 "register_operand" "=wa") + (neg:IEEE128 + (abs:IEEE128 + (match_operand:IEEE128 1 "register_operand" "wa")))) + (clobber (match_scratch:V16QI 2 "=v"))] + "TARGET_FLOAT128_TYPE && !TARGET_FLOAT128_HW + && FLOAT128_IEEE_P (<MODE>mode)" + "#" + "&& 1" + [(parallel [(set (match_dup 0) + (neg:IEEE128 (abs:IEEE128 (match_dup 1)))) + (use (match_dup 2))])] +{ + if (GET_CODE (operands[2]) == SCRATCH) + operands[2] = gen_reg_rtx (V16QImode); + + operands[3] = gen_reg_rtx (V16QImode); + emit_insn (gen_ieee_128bit_negative_zero (operands[2])); +} + [(set_attr "length" "8") + (set_attr "type" "vecsimple")]) + +(define_insn "*ieee_128bit_vsx_nabs<mode>2_internal" + [(set (match_operand:IEEE128 0 "register_operand" "=wa") + (neg:IEEE128 + (abs:IEEE128 + (match_operand:IEEE128 1 "register_operand" "wa")))) + (use (match_operand:V16QI 2 "register_operand" "v"))] + "TARGET_FLOAT128_TYPE && !TARGET_FLOAT128_HW" + "xxlor %x0,%x1,%x2" + [(set_attr "type" "veclogical")]) + +;; Float128 conversion functions. These expand to library function calls. +;; We use expand to convert from IBM double double to IEEE 128-bit +;; and trunc for the opposite. +(define_expand "extendiftf2" + [(set (match_operand:TF 0 "gpc_reg_operand" "") + (float_extend:TF (match_operand:IF 1 "gpc_reg_operand" "")))] + "TARGET_FLOAT128_TYPE" +{ + rs6000_expand_float128_convert (operands[0], operands[1], false); + DONE; +}) + +(define_expand "extendifkf2" + [(set (match_operand:KF 0 "gpc_reg_operand" "") + (float_extend:KF (match_operand:IF 1 "gpc_reg_operand" "")))] + "TARGET_FLOAT128_TYPE" +{ + rs6000_expand_float128_convert (operands[0], operands[1], false); + DONE; +}) + +(define_expand "extendtfkf2" + [(set (match_operand:KF 0 "gpc_reg_operand" "") + (float_extend:KF (match_operand:TF 1 "gpc_reg_operand" "")))] + "TARGET_FLOAT128_TYPE" +{ + rs6000_expand_float128_convert (operands[0], operands[1], false); + DONE; +}) + +(define_expand "trunciftf2" + [(set (match_operand:IF 0 "gpc_reg_operand" "") + (float_truncate:IF (match_operand:TF 1 "gpc_reg_operand" "")))] + "TARGET_FLOAT128_TYPE" +{ + rs6000_expand_float128_convert (operands[0], operands[1], false); + DONE; +}) + +(define_expand "truncifkf2" + [(set (match_operand:IF 0 "gpc_reg_operand" "") + (float_truncate:IF (match_operand:KF 1 "gpc_reg_operand" "")))] + "TARGET_FLOAT128_TYPE" +{ + rs6000_expand_float128_convert (operands[0], operands[1], false); + DONE; +}) + +(define_expand "trunckftf2" + [(set (match_operand:TF 0 "gpc_reg_operand" "") + (float_truncate:TF (match_operand:KF 1 "gpc_reg_operand" "")))] + "TARGET_FLOAT128_TYPE" +{ + rs6000_expand_float128_convert (operands[0], operands[1], false); + DONE; +}) + +(define_expand "trunctfif2" + [(set (match_operand:IF 0 "gpc_reg_operand" "") + (float_truncate:IF (match_operand:TF 1 "gpc_reg_operand" "")))] + "TARGET_FLOAT128_TYPE" +{ + rs6000_expand_float128_convert (operands[0], operands[1], false); + DONE; +}) + + +;; Reload helper functions used by rs6000_secondary_reload. The patterns all +;; must have 3 arguments, and scratch register constraint must be a single +;; constraint. + +;; Reload patterns to support gpr load/store with misaligned mem. +;; and multiple gpr load/store at offset >= 0xfffc +(define_expand "reload_<mode>_store" + [(parallel [(match_operand 0 "memory_operand" "=m") + (match_operand 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "register_operand" "=&b")])] + "" +{ + rs6000_secondary_reload_gpr (operands[1], operands[0], operands[2], true); + DONE; +}) + +(define_expand "reload_<mode>_load" + [(parallel [(match_operand 0 "gpc_reg_operand" "=r") + (match_operand 1 "memory_operand" "m") + (match_operand:GPR 2 "register_operand" "=b")])] + "" +{ + rs6000_secondary_reload_gpr (operands[0], operands[1], operands[2], false); + DONE; +}) + + +;; Reload patterns for various types using the vector registers. We may need +;; an additional base register to convert the reg+offset addressing to reg+reg +;; for vector registers and reg+reg or (reg+reg)&(-16) addressing to just an +;; index register for gpr registers. +(define_expand "reload_<RELOAD:mode>_<P:mptrsize>_store" + [(parallel [(match_operand:RELOAD 0 "memory_operand" "m") + (match_operand:RELOAD 1 "gpc_reg_operand" "wa") + (match_operand:P 2 "register_operand" "=b")])] + "<P:tptrsize>" +{ + rs6000_secondary_reload_inner (operands[1], operands[0], operands[2], true); + DONE; +}) + +(define_expand "reload_<RELOAD:mode>_<P:mptrsize>_load" + [(parallel [(match_operand:RELOAD 0 "gpc_reg_operand" "wa") + (match_operand:RELOAD 1 "memory_operand" "m") + (match_operand:P 2 "register_operand" "=b")])] + "<P:tptrsize>" +{ + rs6000_secondary_reload_inner (operands[0], operands[1], operands[2], false); + DONE; +}) + + +;; Reload sometimes tries to move the address to a GPR, and can generate +;; invalid RTL for addresses involving AND -16. Allow addresses involving +;; reg+reg, reg+small constant, or just reg, all wrapped in an AND -16. + +(define_insn_and_split "*vec_reload_and_plus_<mptrsize>" + [(set (match_operand:P 0 "gpc_reg_operand" "=b") + (and:P (plus:P (match_operand:P 1 "gpc_reg_operand" "r") + (match_operand:P 2 "reg_or_cint_operand" "rI")) + (const_int -16)))] + "TARGET_ALTIVEC && (reload_in_progress || reload_completed)" + "#" + "&& reload_completed" + [(set (match_dup 0) + (plus:P (match_dup 1) + (match_dup 2))) + (set (match_dup 0) + (and:P (match_dup 0) + (const_int -16)))]) + +;; Power8 merge instructions to allow direct move to/from floating point +;; registers in 32-bit mode. We use TF mode to get two registers to move the +;; individual 32-bit parts across. Subreg doesn't work too well on the TF +;; value, since it is allocated in reload and not all of the flow information +;; is setup for it. We have two patterns to do the two moves between gprs and +;; fprs. There isn't a dependancy between the two, but we could potentially +;; schedule other instructions between the two instructions. + +(define_insn "p8_fmrgow_<mode>" + [(set (match_operand:FMOVE64X 0 "register_operand" "=d") + (unspec:FMOVE64X [ + (match_operand:DF 1 "register_operand" "d") + (match_operand:DF 2 "register_operand" "d")] + UNSPEC_P8V_FMRGOW))] + "!TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "fmrgow %0,%1,%2" + [(set_attr "type" "fpsimple")]) + +(define_insn "p8_mtvsrwz" + [(set (match_operand:DF 0 "register_operand" "=d") + (unspec:DF [(match_operand:SI 1 "register_operand" "r")] + UNSPEC_P8V_MTVSRWZ))] + "!TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "mtvsrwz %x0,%1" + [(set_attr "type" "mftgpr")]) + +(define_insn_and_split "reload_fpr_from_gpr<mode>" + [(set (match_operand:FMOVE64X 0 "register_operand" "=d") + (unspec:FMOVE64X [(match_operand:FMOVE64X 1 "register_operand" "r")] + UNSPEC_P8V_RELOAD_FROM_GPR)) + (clobber (match_operand:IF 2 "register_operand" "=d"))] + "!TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx tmp_hi = simplify_gen_subreg (DFmode, operands[2], IFmode, 0); + rtx tmp_lo = simplify_gen_subreg (DFmode, operands[2], IFmode, 8); + rtx gpr_hi_reg = gen_highpart (SImode, src); + rtx gpr_lo_reg = gen_lowpart (SImode, src); + + emit_insn (gen_p8_mtvsrwz (tmp_hi, gpr_hi_reg)); + emit_insn (gen_p8_mtvsrwz (tmp_lo, gpr_lo_reg)); + emit_insn (gen_p8_fmrgow_<mode> (dest, tmp_hi, tmp_lo)); + DONE; +} + [(set_attr "length" "12") + (set_attr "type" "three")]) + +;; Move 128 bit values from GPRs to VSX registers in 64-bit mode +(define_insn "p8_mtvsrd_df" + [(set (match_operand:DF 0 "register_operand" "=wa") + (unspec:DF [(match_operand:DI 1 "register_operand" "r")] + UNSPEC_P8V_MTVSRD))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "mtvsrd %x0,%1" + [(set_attr "type" "mftgpr")]) + +(define_insn "p8_xxpermdi_<mode>" + [(set (match_operand:FMOVE128_GPR 0 "register_operand" "=wa") + (unspec:FMOVE128_GPR [ + (match_operand:DF 1 "register_operand" "wa") + (match_operand:DF 2 "register_operand" "wa")] + UNSPEC_P8V_XXPERMDI))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "xxpermdi %x0,%x1,%x2,0" + [(set_attr "type" "vecperm")]) + +(define_insn_and_split "reload_vsx_from_gpr<mode>" + [(set (match_operand:FMOVE128_GPR 0 "register_operand" "=wa") + (unspec:FMOVE128_GPR + [(match_operand:FMOVE128_GPR 1 "register_operand" "r")] + UNSPEC_P8V_RELOAD_FROM_GPR)) + (clobber (match_operand:IF 2 "register_operand" "=wa"))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + /* You might think that we could use op0 as one temp and a DF clobber + as op2, but you'd be wrong. Secondary reload move patterns don't + check for overlap of the clobber and the destination. */ + rtx tmp_hi = simplify_gen_subreg (DFmode, operands[2], IFmode, 0); + rtx tmp_lo = simplify_gen_subreg (DFmode, operands[2], IFmode, 8); + rtx gpr_hi_reg = gen_highpart (DImode, src); + rtx gpr_lo_reg = gen_lowpart (DImode, src); + + emit_insn (gen_p8_mtvsrd_df (tmp_hi, gpr_hi_reg)); + emit_insn (gen_p8_mtvsrd_df (tmp_lo, gpr_lo_reg)); + emit_insn (gen_p8_xxpermdi_<mode> (dest, tmp_hi, tmp_lo)); + DONE; +} + [(set_attr "length" "12") + (set_attr "type" "three")]) + +(define_split + [(set (match_operand:FMOVE128_GPR 0 "nonimmediate_operand" "") + (match_operand:FMOVE128_GPR 1 "input_operand" ""))] + "reload_completed + && (int_reg_operand (operands[0], <MODE>mode) + || int_reg_operand (operands[1], <MODE>mode)) + && (!TARGET_DIRECT_MOVE_128 + || (!vsx_register_operand (operands[0], <MODE>mode) + && !vsx_register_operand (operands[1], <MODE>mode)))" + [(pc)] +{ rs6000_split_multireg_move (operands[0], operands[1]); DONE; }) + +;; Move SFmode to a VSX from a GPR register. Because scalar floating point +;; type is stored internally as double precision in the VSX registers, we have +;; to convert it from the vector format. +(define_insn "p8_mtvsrd_sf" + [(set (match_operand:SF 0 "register_operand" "=wa") + (unspec:SF [(match_operand:DI 1 "register_operand" "r")] + UNSPEC_P8V_MTVSRD))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "mtvsrd %x0,%1" + [(set_attr "type" "mftgpr")]) + +(define_insn_and_split "reload_vsx_from_gprsf" + [(set (match_operand:SF 0 "register_operand" "=wa") + (unspec:SF [(match_operand:SF 1 "register_operand" "r")] + UNSPEC_P8V_RELOAD_FROM_GPR)) + (clobber (match_operand:DI 2 "register_operand" "=r"))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + rtx op2 = operands[2]; + rtx op1_di = simplify_gen_subreg (DImode, op1, SFmode, 0); + + /* Move SF value to upper 32-bits for xscvspdpn. */ + emit_insn (gen_ashldi3 (op2, op1_di, GEN_INT (32))); + emit_insn (gen_p8_mtvsrd_sf (op0, op2)); + emit_insn (gen_vsx_xscvspdpn_directmove (op0, op0)); + DONE; +} + [(set_attr "length" "8") + (set_attr "type" "two")]) + +;; Move 128 bit values from VSX registers to GPRs in 64-bit mode by doing a +;; normal 64-bit move, followed by an xxpermdi to get the bottom 64-bit value, +;; and then doing a move of that. +(define_insn "p8_mfvsrd_3_<mode>" + [(set (match_operand:DF 0 "register_operand" "=r") + (unspec:DF [(match_operand:FMOVE128_GPR 1 "register_operand" "wa")] + UNSPEC_P8V_RELOAD_FROM_VSX))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "mfvsrd %0,%x1" + [(set_attr "type" "mftgpr")]) + +(define_insn_and_split "reload_gpr_from_vsx<mode>" + [(set (match_operand:FMOVE128_GPR 0 "register_operand" "=r") + (unspec:FMOVE128_GPR + [(match_operand:FMOVE128_GPR 1 "register_operand" "wa")] + UNSPEC_P8V_RELOAD_FROM_VSX)) + (clobber (match_operand:FMOVE128_GPR 2 "register_operand" "=wa"))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx tmp = operands[2]; + rtx gpr_hi_reg = gen_highpart (DFmode, dest); + rtx gpr_lo_reg = gen_lowpart (DFmode, dest); + + emit_insn (gen_p8_mfvsrd_3_<mode> (gpr_hi_reg, src)); + emit_insn (gen_vsx_xxpermdi_<mode>_be (tmp, src, src, GEN_INT (3))); + emit_insn (gen_p8_mfvsrd_3_<mode> (gpr_lo_reg, tmp)); + DONE; +} + [(set_attr "length" "12") + (set_attr "type" "three")]) + +;; Move SFmode to a GPR from a VSX register. Because scalar floating point +;; type is stored internally as double precision, we have to convert it to the +;; vector format. + +(define_insn_and_split "reload_gpr_from_vsxsf" + [(set (match_operand:SF 0 "register_operand" "=r") + (unspec:SF [(match_operand:SF 1 "register_operand" "wa")] + UNSPEC_P8V_RELOAD_FROM_VSX)) + (clobber (match_operand:V4SF 2 "register_operand" "=wa"))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + rtx op2 = operands[2]; + rtx diop0 = simplify_gen_subreg (DImode, op0, SFmode, 0); + + emit_insn (gen_vsx_xscvdpspn_scalar (op2, op1)); + emit_insn (gen_p8_mfvsrd_4_disf (diop0, op2)); + emit_insn (gen_lshrdi3 (diop0, diop0, GEN_INT (32))); + DONE; +} + [(set_attr "length" "12") + (set_attr "type" "three")]) + +(define_insn "p8_mfvsrd_4_disf" + [(set (match_operand:DI 0 "register_operand" "=r") + (unspec:DI [(match_operand:V4SF 1 "register_operand" "wa")] + UNSPEC_P8V_RELOAD_FROM_VSX))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE" + "mfvsrd %0,%x1" + [(set_attr "type" "mftgpr")]) + + +;; Next come the multi-word integer load and store and the load and store +;; multiple insns. + +;; List r->r after r->Y, otherwise reload will try to reload a +;; non-offsettable address by using r->r which won't make progress. +;; Use of fprs is disparaged slightly otherwise reload prefers to reload +;; a gpr into a fpr instead of reloading an invalid 'Y' address + +;; GPR store GPR load GPR move FPR store FPR load FPR move +;; GPR const AVX store AVX store AVX load AVX load VSX move +;; P9 0 P9 -1 AVX 0/-1 VSX 0 VSX -1 P9 const +;; AVX const + +(define_insn "*movdi_internal32" + [(set (match_operand:DI 0 "rs6000_nonimmediate_operand" + "=Y, r, r, ^m, ^d, ^d, + r, ^wY, $Z, ^wb, $wv, ^wi, + *wo, *wo, *wv, *wi, *wi, *wv, + *wv") + + (match_operand:DI 1 "input_operand" + "r, Y, r, d, m, d, + IJKnGHF, wb, wv, wY, Z, wi, + Oj, wM, OjwM, Oj, wM, wS, + wB"))] + + "! TARGET_POWERPC64 + && (gpc_reg_operand (operands[0], DImode) + || gpc_reg_operand (operands[1], DImode))" + "@ + # + # + # + stfd%U0%X0 %1,%0 + lfd%U1%X1 %0,%1 + fmr %0,%1 + # + stxsd %1,%0 + stxsdx %x1,%y0 + lxsd %0,%1 + lxsdx %x0,%y1 + xxlor %x0,%x1,%x1 + xxspltib %x0,0 + xxspltib %x0,255 + vspltisw %0,%1 + xxlxor %x0,%x0,%x0 + xxlorc %x0,%x0,%x0 + # + #" + [(set_attr "type" + "store, load, *, fpstore, fpload, fpsimple, + *, fpstore, fpstore, fpload, fpload, veclogical, + vecsimple, vecsimple, vecsimple, veclogical, veclogical, vecsimple, + vecsimple") + (set_attr "size" "64")]) + +(define_split + [(set (match_operand:DI 0 "gpc_reg_operand" "") + (match_operand:DI 1 "const_int_operand" ""))] + "! TARGET_POWERPC64 && reload_completed + && gpr_or_gpr_p (operands[0], operands[1]) + && !direct_move_p (operands[0], operands[1])" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 1))] + " +{ + HOST_WIDE_INT value = INTVAL (operands[1]); + operands[2] = operand_subword_force (operands[0], WORDS_BIG_ENDIAN == 0, + DImode); + operands[3] = operand_subword_force (operands[0], WORDS_BIG_ENDIAN != 0, + DImode); + operands[4] = GEN_INT (value >> 32); + operands[1] = GEN_INT (((value & 0xffffffff) ^ 0x80000000) - 0x80000000); +}") + +(define_split + [(set (match_operand:DIFD 0 "rs6000_nonimmediate_operand" "") + (match_operand:DIFD 1 "input_operand" ""))] + "reload_completed && !TARGET_POWERPC64 + && gpr_or_gpr_p (operands[0], operands[1]) + && !direct_move_p (operands[0], operands[1])" + [(pc)] +{ rs6000_split_multireg_move (operands[0], operands[1]); DONE; }) + +;; GPR store GPR load GPR move GPR li GPR lis GPR # +;; FPR store FPR load FPR move AVX store AVX store AVX load +;; AVX load VSX move P9 0 P9 -1 AVX 0/-1 VSX 0 +;; VSX -1 P9 const AVX const From SPR To SPR SPR<->SPR +;; FPR->GPR GPR->FPR VSX->GPR GPR->VSX +(define_insn "*movdi_internal64" + [(set (match_operand:DI 0 "nonimmediate_operand" + "=Y, r, r, r, r, r, + ^m, ^d, ^d, ^wY, $Z, $wb, + $wv, ^wi, *wo, *wo, *wv, *wi, + *wi, *wv, *wv, r, *h, *h, + ?*r, ?*wg, ?*r, ?*wj") + + (match_operand:DI 1 "input_operand" + "r, Y, r, I, L, nF, + d, m, d, wb, wv, wY, + Z, wi, Oj, wM, OjwM, Oj, + wM, wS, wB, *h, r, 0, + wg, r, wj, r"))] + + "TARGET_POWERPC64 + && (gpc_reg_operand (operands[0], DImode) + || gpc_reg_operand (operands[1], DImode))" + "@ + std%U0%X0 %1,%0 + ld%U1%X1 %0,%1 + mr %0,%1 + li %0,%1 + lis %0,%v1 + # + stfd%U0%X0 %1,%0 + lfd%U1%X1 %0,%1 + fmr %0,%1 + stxsd %1,%0 + stxsdx %x1,%y0 + lxsd %0,%1 + lxsdx %x0,%y1 + xxlor %x0,%x1,%x1 + xxspltib %x0,0 + xxspltib %x0,255 + # + xxlxor %x0,%x0,%x0 + xxlorc %x0,%x0,%x0 + # + # + mf%1 %0 + mt%0 %1 + nop + mftgpr %0,%1 + mffgpr %0,%1 + mfvsrd %0,%x1 + mtvsrd %x0,%1" + [(set_attr "type" + "store, load, *, *, *, *, + fpstore, fpload, fpsimple, fpstore, fpstore, fpload, + fpload, veclogical, vecsimple, vecsimple, vecsimple, veclogical, + veclogical, vecsimple, vecsimple, mfjmpr, mtjmpr, *, + mftgpr, mffgpr, mftgpr, mffgpr") + + (set_attr "size" "64") + (set_attr "length" + "4, 4, 4, 4, 4, 20, + 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 8, + 8, 4, 4, 4, 4, 4, + 4, 4, 4, 4")]) + +; Some DImode loads are best done as a load of -1 followed by a mask +; instruction. +(define_split + [(set (match_operand:DI 0 "int_reg_operand_not_pseudo") + (match_operand:DI 1 "const_int_operand"))] + "TARGET_POWERPC64 + && num_insns_constant (operands[1], DImode) > 1 + && !IN_RANGE (INTVAL (operands[1]), -0x80000000, 0xffffffff) + && rs6000_is_valid_and_mask (operands[1], DImode)" + [(set (match_dup 0) + (const_int -1)) + (set (match_dup 0) + (and:DI (match_dup 0) + (match_dup 1)))] + "") + +;; Split a load of a large constant into the appropriate five-instruction +;; sequence. Handle anything in a constant number of insns. +;; When non-easy constants can go in the TOC, this should use +;; easy_fp_constant predicate. +(define_split + [(set (match_operand:DI 0 "int_reg_operand_not_pseudo" "") + (match_operand:DI 1 "const_int_operand" ""))] + "TARGET_POWERPC64 && num_insns_constant (operands[1], DImode) > 1" + [(set (match_dup 0) (match_dup 2)) + (set (match_dup 0) (plus:DI (match_dup 0) (match_dup 3)))] + " +{ + if (rs6000_emit_set_const (operands[0], operands[1])) + DONE; + else + FAIL; +}") + +(define_split + [(set (match_operand:DI 0 "int_reg_operand_not_pseudo" "") + (match_operand:DI 1 "const_scalar_int_operand" ""))] + "TARGET_POWERPC64 && num_insns_constant (operands[1], DImode) > 1" + [(set (match_dup 0) (match_dup 2)) + (set (match_dup 0) (plus:DI (match_dup 0) (match_dup 3)))] + " +{ + if (rs6000_emit_set_const (operands[0], operands[1])) + DONE; + else + FAIL; +}") + +(define_split + [(set (match_operand:DI 0 "altivec_register_operand" "") + (match_operand:DI 1 "s5bit_cint_operand" ""))] + "TARGET_UPPER_REGS_DI && TARGET_VSX && reload_completed" + [(const_int 0)] +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + int r = REGNO (op0); + rtx op0_v4si = gen_rtx_REG (V4SImode, r); + + emit_insn (gen_altivec_vspltisw (op0_v4si, op1)); + if (op1 != const0_rtx && op1 != constm1_rtx) + { + rtx op0_v2di = gen_rtx_REG (V2DImode, r); + emit_insn (gen_altivec_vupkhsw (op0_v2di, op0_v4si)); + } + DONE; +}) + +;; Split integer constants that can be loaded with XXSPLTIB and a +;; sign extend operation. +(define_split + [(set (match_operand:INT_ISA3 0 "altivec_register_operand" "") + (match_operand:INT_ISA3 1 "xxspltib_constant_split" ""))] + "TARGET_UPPER_REGS_DI && TARGET_P9_VECTOR && reload_completed" + [(const_int 0)] +{ + rtx op0 = operands[0]; + rtx op1 = operands[1]; + int r = REGNO (op0); + rtx op0_v16qi = gen_rtx_REG (V16QImode, r); + + emit_insn (gen_xxspltib_v16qi (op0_v16qi, op1)); + if (<MODE>mode == DImode) + emit_insn (gen_vsx_sign_extend_qi_di (operands[0], op0_v16qi)); + else if (<MODE>mode == SImode) + emit_insn (gen_vsx_sign_extend_qi_si (operands[0], op0_v16qi)); + else if (<MODE>mode == HImode) + { + rtx op0_v8hi = gen_rtx_REG (V8HImode, r); + emit_insn (gen_altivec_vupkhsb (op0_v8hi, op0_v16qi)); + } + DONE; +}) + + +;; TImode/PTImode is similar, except that we usually want to compute the +;; address into a register and use lsi/stsi (the exception is during reload). + +(define_insn "*mov<mode>_string" + [(set (match_operand:TI2 0 "reg_or_mem_operand" "=Q,Y,????r,????r,????r,r") + (match_operand:TI2 1 "input_operand" "r,r,Q,Y,r,n"))] + "! TARGET_POWERPC64 + && (<MODE>mode != TImode || VECTOR_MEM_NONE_P (TImode)) + && (gpc_reg_operand (operands[0], <MODE>mode) + || gpc_reg_operand (operands[1], <MODE>mode))" + "* +{ + switch (which_alternative) + { + default: + gcc_unreachable (); + case 0: + if (TARGET_STRING) + return \"stswi %1,%P0,16\"; + /* FALLTHRU */ + case 1: + return \"#\"; + case 2: + /* If the address is not used in the output, we can use lsi. Otherwise, + fall through to generating four loads. */ + if (TARGET_STRING + && ! reg_overlap_mentioned_p (operands[0], operands[1])) + return \"lswi %0,%P1,16\"; + /* fall through */ + case 3: + case 4: + case 5: + return \"#\"; + } +}" + [(set_attr "type" "store,store,load,load,*,*") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set (attr "cell_micro") (if_then_else (match_test "TARGET_STRING") + (const_string "always") + (const_string "conditional")))]) + +(define_insn "*mov<mode>_ppc64" + [(set (match_operand:TI2 0 "nonimmediate_operand" "=wQ,Y,r,r,r,r") + (match_operand:TI2 1 "input_operand" "r,r,wQ,Y,r,n"))] + "(TARGET_POWERPC64 && VECTOR_MEM_NONE_P (<MODE>mode) + && (gpc_reg_operand (operands[0], <MODE>mode) + || gpc_reg_operand (operands[1], <MODE>mode)))" +{ + return rs6000_output_move_128bit (operands); +} + [(set_attr "type" "store,store,load,load,*,*") + (set_attr "length" "8")]) + +(define_split + [(set (match_operand:TI2 0 "int_reg_operand" "") + (match_operand:TI2 1 "const_scalar_int_operand" ""))] + "TARGET_POWERPC64 + && (VECTOR_MEM_NONE_P (<MODE>mode) + || (reload_completed && INT_REGNO_P (REGNO (operands[0]))))" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] + " +{ + operands[2] = operand_subword_force (operands[0], WORDS_BIG_ENDIAN == 0, + <MODE>mode); + operands[3] = operand_subword_force (operands[0], WORDS_BIG_ENDIAN != 0, + <MODE>mode); + if (CONST_WIDE_INT_P (operands[1])) + { + operands[4] = GEN_INT (CONST_WIDE_INT_ELT (operands[1], 1)); + operands[5] = GEN_INT (CONST_WIDE_INT_ELT (operands[1], 0)); + } + else if (CONST_INT_P (operands[1])) + { + operands[4] = GEN_INT (- (INTVAL (operands[1]) < 0)); + operands[5] = operands[1]; + } + else + FAIL; +}") + +(define_split + [(set (match_operand:TI2 0 "nonimmediate_operand" "") + (match_operand:TI2 1 "input_operand" ""))] + "reload_completed + && gpr_or_gpr_p (operands[0], operands[1]) + && !direct_move_p (operands[0], operands[1]) + && !quad_load_store_p (operands[0], operands[1])" + [(pc)] +{ rs6000_split_multireg_move (operands[0], operands[1]); DONE; }) + +(define_expand "load_multiple" + [(match_par_dup 3 [(set (match_operand:SI 0 "" "") + (match_operand:SI 1 "" "")) + (use (match_operand:SI 2 "" ""))])] + "TARGET_STRING && !TARGET_POWERPC64" + " +{ + int regno; + int count; + rtx op1; + int i; + + /* Support only loading a constant number of fixed-point registers from + memory and only bother with this if more than two; the machine + doesn't support more than eight. */ + if (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) <= 2 + || INTVAL (operands[2]) > 8 + || GET_CODE (operands[1]) != MEM + || GET_CODE (operands[0]) != REG + || REGNO (operands[0]) >= 32) + FAIL; + + count = INTVAL (operands[2]); + regno = REGNO (operands[0]); + + operands[3] = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); + op1 = replace_equiv_address (operands[1], + force_reg (SImode, XEXP (operands[1], 0))); + + for (i = 0; i < count; i++) + XVECEXP (operands[3], 0, i) + = gen_rtx_SET (gen_rtx_REG (SImode, regno + i), + adjust_address_nv (op1, SImode, i * 4)); +}") + +(define_insn "*ldmsi8" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 2 "gpc_reg_operand" "") + (mem:SI (match_operand:SI 1 "gpc_reg_operand" "b"))) + (set (match_operand:SI 3 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 4)))) + (set (match_operand:SI 4 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 8)))) + (set (match_operand:SI 5 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 12)))) + (set (match_operand:SI 6 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 16)))) + (set (match_operand:SI 7 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 20)))) + (set (match_operand:SI 8 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 24)))) + (set (match_operand:SI 9 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 28))))])] + "TARGET_STRING && XVECLEN (operands[0], 0) == 8" + "* +{ return rs6000_output_load_multiple (operands); }" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "length" "32")]) + +(define_insn "*ldmsi7" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 2 "gpc_reg_operand" "") + (mem:SI (match_operand:SI 1 "gpc_reg_operand" "b"))) + (set (match_operand:SI 3 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 4)))) + (set (match_operand:SI 4 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 8)))) + (set (match_operand:SI 5 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 12)))) + (set (match_operand:SI 6 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 16)))) + (set (match_operand:SI 7 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 20)))) + (set (match_operand:SI 8 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 24))))])] + "TARGET_STRING && XVECLEN (operands[0], 0) == 7" + "* +{ return rs6000_output_load_multiple (operands); }" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "length" "32")]) + +(define_insn "*ldmsi6" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 2 "gpc_reg_operand" "") + (mem:SI (match_operand:SI 1 "gpc_reg_operand" "b"))) + (set (match_operand:SI 3 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 4)))) + (set (match_operand:SI 4 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 8)))) + (set (match_operand:SI 5 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 12)))) + (set (match_operand:SI 6 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 16)))) + (set (match_operand:SI 7 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 20))))])] + "TARGET_STRING && XVECLEN (operands[0], 0) == 6" + "* +{ return rs6000_output_load_multiple (operands); }" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "length" "32")]) + +(define_insn "*ldmsi5" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 2 "gpc_reg_operand" "") + (mem:SI (match_operand:SI 1 "gpc_reg_operand" "b"))) + (set (match_operand:SI 3 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 4)))) + (set (match_operand:SI 4 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 8)))) + (set (match_operand:SI 5 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 12)))) + (set (match_operand:SI 6 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 16))))])] + "TARGET_STRING && XVECLEN (operands[0], 0) == 5" + "* +{ return rs6000_output_load_multiple (operands); }" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "length" "32")]) + +(define_insn "*ldmsi4" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 2 "gpc_reg_operand" "") + (mem:SI (match_operand:SI 1 "gpc_reg_operand" "b"))) + (set (match_operand:SI 3 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 4)))) + (set (match_operand:SI 4 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 8)))) + (set (match_operand:SI 5 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 12))))])] + "TARGET_STRING && XVECLEN (operands[0], 0) == 4" + "* +{ return rs6000_output_load_multiple (operands); }" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "length" "32")]) + +(define_insn "*ldmsi3" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 2 "gpc_reg_operand" "") + (mem:SI (match_operand:SI 1 "gpc_reg_operand" "b"))) + (set (match_operand:SI 3 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 4)))) + (set (match_operand:SI 4 "gpc_reg_operand" "") + (mem:SI (plus:SI (match_dup 1) (const_int 8))))])] + "TARGET_STRING && XVECLEN (operands[0], 0) == 3" + "* +{ return rs6000_output_load_multiple (operands); }" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "length" "32")]) + +(define_expand "store_multiple" + [(match_par_dup 3 [(set (match_operand:SI 0 "" "") + (match_operand:SI 1 "" "")) + (clobber (scratch:SI)) + (use (match_operand:SI 2 "" ""))])] + "TARGET_STRING && !TARGET_POWERPC64" + " +{ + int regno; + int count; + rtx to; + rtx op0; + int i; + + /* Support only storing a constant number of fixed-point registers to + memory and only bother with this if more than two; the machine + doesn't support more than eight. */ + if (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) <= 2 + || INTVAL (operands[2]) > 8 + || GET_CODE (operands[0]) != MEM + || GET_CODE (operands[1]) != REG + || REGNO (operands[1]) >= 32) + FAIL; + + count = INTVAL (operands[2]); + regno = REGNO (operands[1]); + + operands[3] = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count + 1)); + to = force_reg (SImode, XEXP (operands[0], 0)); + op0 = replace_equiv_address (operands[0], to); + + XVECEXP (operands[3], 0, 0) + = gen_rtx_SET (adjust_address_nv (op0, SImode, 0), operands[1]); + XVECEXP (operands[3], 0, 1) = gen_rtx_CLOBBER (VOIDmode, + gen_rtx_SCRATCH (SImode)); + + for (i = 1; i < count; i++) + XVECEXP (operands[3], 0, i + 1) + = gen_rtx_SET (adjust_address_nv (op0, SImode, i * 4), + gen_rtx_REG (SImode, regno + i)); +}") + +(define_insn "*stmsi8" + [(match_parallel 0 "store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "gpc_reg_operand" "b")) + (match_operand:SI 2 "gpc_reg_operand" "r")) + (clobber (match_scratch:SI 3 "=X")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 4 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 5 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 6 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 16))) + (match_operand:SI 7 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 20))) + (match_operand:SI 8 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 24))) + (match_operand:SI 9 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 28))) + (match_operand:SI 10 "gpc_reg_operand" "r"))])] + "TARGET_STRING && XVECLEN (operands[0], 0) == 9" + "stswi %2,%1,%O0" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "cell_micro" "always")]) + +(define_insn "*stmsi7" + [(match_parallel 0 "store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "gpc_reg_operand" "b")) + (match_operand:SI 2 "gpc_reg_operand" "r")) + (clobber (match_scratch:SI 3 "=X")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 4 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 5 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 6 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 16))) + (match_operand:SI 7 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 20))) + (match_operand:SI 8 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 24))) + (match_operand:SI 9 "gpc_reg_operand" "r"))])] + "TARGET_STRING && XVECLEN (operands[0], 0) == 8" + "stswi %2,%1,%O0" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "cell_micro" "always")]) + +(define_insn "*stmsi6" + [(match_parallel 0 "store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "gpc_reg_operand" "b")) + (match_operand:SI 2 "gpc_reg_operand" "r")) + (clobber (match_scratch:SI 3 "=X")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 4 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 5 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 6 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 16))) + (match_operand:SI 7 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 20))) + (match_operand:SI 8 "gpc_reg_operand" "r"))])] + "TARGET_STRING && XVECLEN (operands[0], 0) == 7" + "stswi %2,%1,%O0" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "cell_micro" "always")]) + +(define_insn "*stmsi5" + [(match_parallel 0 "store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "gpc_reg_operand" "b")) + (match_operand:SI 2 "gpc_reg_operand" "r")) + (clobber (match_scratch:SI 3 "=X")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 4 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 5 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 6 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 16))) + (match_operand:SI 7 "gpc_reg_operand" "r"))])] + "TARGET_STRING && XVECLEN (operands[0], 0) == 6" + "stswi %2,%1,%O0" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "cell_micro" "always")]) + +(define_insn "*stmsi4" + [(match_parallel 0 "store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "gpc_reg_operand" "b")) + (match_operand:SI 2 "gpc_reg_operand" "r")) + (clobber (match_scratch:SI 3 "=X")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 4 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 5 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 12))) + (match_operand:SI 6 "gpc_reg_operand" "r"))])] + "TARGET_STRING && XVECLEN (operands[0], 0) == 5" + "stswi %2,%1,%O0" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "cell_micro" "always")]) + +(define_insn "*stmsi3" + [(match_parallel 0 "store_multiple_operation" + [(set (mem:SI (match_operand:SI 1 "gpc_reg_operand" "b")) + (match_operand:SI 2 "gpc_reg_operand" "r")) + (clobber (match_scratch:SI 3 "=X")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 4))) + (match_operand:SI 4 "gpc_reg_operand" "r")) + (set (mem:SI (plus:SI (match_dup 1) (const_int 8))) + (match_operand:SI 5 "gpc_reg_operand" "r"))])] + "TARGET_STRING && XVECLEN (operands[0], 0) == 4" + "stswi %2,%1,%O0" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "cell_micro" "always")]) + +(define_expand "setmemsi" + [(parallel [(set (match_operand:BLK 0 "" "") + (match_operand 2 "const_int_operand" "")) + (use (match_operand:SI 1 "" "")) + (use (match_operand:SI 3 "" ""))])] + "" + " +{ + /* If value to set is not zero, use the library routine. */ + if (operands[2] != const0_rtx) + FAIL; + + if (expand_block_clear (operands)) + DONE; + else + FAIL; +}") + +;; String compare N insn. +;; Argument 0 is the target (result) +;; Argument 1 is the destination +;; Argument 2 is the source +;; Argument 3 is the length +;; Argument 4 is the alignment + +(define_expand "cmpstrnsi" + [(parallel [(set (match_operand:SI 0) + (compare:SI (match_operand:BLK 1) + (match_operand:BLK 2))) + (use (match_operand:SI 3)) + (use (match_operand:SI 4))])] + "TARGET_CMPB && (BYTES_BIG_ENDIAN || TARGET_LDBRX)" +{ + if (optimize_insn_for_size_p ()) + FAIL; + + if (expand_strn_compare (operands, 0)) + DONE; + else + FAIL; +}) + +;; String compare insn. +;; Argument 0 is the target (result) +;; Argument 1 is the destination +;; Argument 2 is the source +;; Argument 3 is the alignment + +(define_expand "cmpstrsi" + [(parallel [(set (match_operand:SI 0) + (compare:SI (match_operand:BLK 1) + (match_operand:BLK 2))) + (use (match_operand:SI 3))])] + "TARGET_CMPB && (BYTES_BIG_ENDIAN || TARGET_LDBRX)" +{ + if (optimize_insn_for_size_p ()) + FAIL; + + if (expand_strn_compare (operands, 1)) + DONE; + else + FAIL; +}) + +;; Block compare insn. +;; Argument 0 is the target (result) +;; Argument 1 is the destination +;; Argument 2 is the source +;; Argument 3 is the length +;; Argument 4 is the alignment + +(define_expand "cmpmemsi" + [(parallel [(set (match_operand:SI 0) + (compare:SI (match_operand:BLK 1) + (match_operand:BLK 2))) + (use (match_operand:SI 3)) + (use (match_operand:SI 4))])] + "TARGET_POPCNTD" +{ + if (expand_block_compare (operands)) + DONE; + else + FAIL; +}) + +;; String/block move insn. +;; Argument 0 is the destination +;; Argument 1 is the source +;; Argument 2 is the length +;; Argument 3 is the alignment + +(define_expand "movmemsi" + [(parallel [(set (match_operand:BLK 0 "" "") + (match_operand:BLK 1 "" "")) + (use (match_operand:SI 2 "" "")) + (use (match_operand:SI 3 "" ""))])] + "" + " +{ + if (expand_block_move (operands)) + DONE; + else + FAIL; +}") + +;; Move up to 32 bytes at a time. The fixed registers are needed because the +;; register allocator doesn't have a clue about allocating 8 word registers. +;; rD/rS = r5 is preferred, efficient form. +(define_expand "movmemsi_8reg" + [(parallel [(set (match_operand 0 "" "") + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (use (match_operand 3 "" "")) + (clobber (reg:SI 5)) + (clobber (reg:SI 6)) + (clobber (reg:SI 7)) + (clobber (reg:SI 8)) + (clobber (reg:SI 9)) + (clobber (reg:SI 10)) + (clobber (reg:SI 11)) + (clobber (reg:SI 12)) + (clobber (match_scratch:SI 4 ""))])] + "TARGET_STRING" + "") + +(define_insn "" + [(set (mem:BLK (match_operand:P 0 "gpc_reg_operand" "b")) + (mem:BLK (match_operand:P 1 "gpc_reg_operand" "b"))) + (use (match_operand:SI 2 "immediate_operand" "i")) + (use (match_operand:SI 3 "immediate_operand" "i")) + (clobber (match_operand:SI 4 "gpc_reg_operand" "=&r")) + (clobber (reg:SI 6)) + (clobber (reg:SI 7)) + (clobber (reg:SI 8)) + (clobber (reg:SI 9)) + (clobber (reg:SI 10)) + (clobber (reg:SI 11)) + (clobber (reg:SI 12)) + (clobber (match_scratch:SI 5 "=X"))] + "TARGET_STRING + && ((INTVAL (operands[2]) > 24 && INTVAL (operands[2]) < 32) + || INTVAL (operands[2]) == 0) + && (REGNO (operands[0]) < 5 || REGNO (operands[0]) > 12) + && (REGNO (operands[1]) < 5 || REGNO (operands[1]) > 12) + && REGNO (operands[4]) == 5" + "lswi %4,%1,%2\;stswi %4,%0,%2" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "cell_micro" "always") + (set_attr "length" "8")]) + +;; Move up to 24 bytes at a time. The fixed registers are needed because the +;; register allocator doesn't have a clue about allocating 6 word registers. +;; rD/rS = r5 is preferred, efficient form. +(define_expand "movmemsi_6reg" + [(parallel [(set (match_operand 0 "" "") + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (use (match_operand 3 "" "")) + (clobber (reg:SI 5)) + (clobber (reg:SI 6)) + (clobber (reg:SI 7)) + (clobber (reg:SI 8)) + (clobber (reg:SI 9)) + (clobber (reg:SI 10)) + (clobber (match_scratch:SI 4 ""))])] + "TARGET_STRING" + "") + +(define_insn "" + [(set (mem:BLK (match_operand:P 0 "gpc_reg_operand" "b")) + (mem:BLK (match_operand:P 1 "gpc_reg_operand" "b"))) + (use (match_operand:SI 2 "immediate_operand" "i")) + (use (match_operand:SI 3 "immediate_operand" "i")) + (clobber (match_operand:SI 4 "gpc_reg_operand" "=&r")) + (clobber (reg:SI 6)) + (clobber (reg:SI 7)) + (clobber (reg:SI 8)) + (clobber (reg:SI 9)) + (clobber (reg:SI 10)) + (clobber (match_scratch:SI 5 "=X"))] + "TARGET_STRING + && INTVAL (operands[2]) > 16 && INTVAL (operands[2]) <= 32 + && (REGNO (operands[0]) < 5 || REGNO (operands[0]) > 10) + && (REGNO (operands[1]) < 5 || REGNO (operands[1]) > 10) + && REGNO (operands[4]) == 5" + "lswi %4,%1,%2\;stswi %4,%0,%2" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "cell_micro" "always") + (set_attr "length" "8")]) + +;; Move up to 16 bytes at a time, using 4 fixed registers to avoid spill +;; problems with TImode. +;; rD/rS = r5 is preferred, efficient form. +(define_expand "movmemsi_4reg" + [(parallel [(set (match_operand 0 "" "") + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (use (match_operand 3 "" "")) + (clobber (reg:SI 5)) + (clobber (reg:SI 6)) + (clobber (reg:SI 7)) + (clobber (reg:SI 8)) + (clobber (match_scratch:SI 4 ""))])] + "TARGET_STRING" + "") + +(define_insn "" + [(set (mem:BLK (match_operand:P 0 "gpc_reg_operand" "b")) + (mem:BLK (match_operand:P 1 "gpc_reg_operand" "b"))) + (use (match_operand:SI 2 "immediate_operand" "i")) + (use (match_operand:SI 3 "immediate_operand" "i")) + (clobber (match_operand:SI 4 "gpc_reg_operand" "=&r")) + (clobber (reg:SI 6)) + (clobber (reg:SI 7)) + (clobber (reg:SI 8)) + (clobber (match_scratch:SI 5 "=X"))] + "TARGET_STRING + && INTVAL (operands[2]) > 8 && INTVAL (operands[2]) <= 16 + && (REGNO (operands[0]) < 5 || REGNO (operands[0]) > 8) + && (REGNO (operands[1]) < 5 || REGNO (operands[1]) > 8) + && REGNO (operands[4]) == 5" + "lswi %4,%1,%2\;stswi %4,%0,%2" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "cell_micro" "always") + (set_attr "length" "8")]) + +;; Move up to 8 bytes at a time. +(define_expand "movmemsi_2reg" + [(parallel [(set (match_operand 0 "" "") + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (use (match_operand 3 "" "")) + (clobber (match_scratch:DI 4 "")) + (clobber (match_scratch:SI 5 ""))])] + "TARGET_STRING && ! TARGET_POWERPC64" + "") + +(define_insn "" + [(set (mem:BLK (match_operand:SI 0 "gpc_reg_operand" "b")) + (mem:BLK (match_operand:SI 1 "gpc_reg_operand" "b"))) + (use (match_operand:SI 2 "immediate_operand" "i")) + (use (match_operand:SI 3 "immediate_operand" "i")) + (clobber (match_scratch:DI 4 "=&r")) + (clobber (match_scratch:SI 5 "=X"))] + "TARGET_STRING && ! TARGET_POWERPC64 + && INTVAL (operands[2]) > 4 && INTVAL (operands[2]) <= 8" + "lswi %4,%1,%2\;stswi %4,%0,%2" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "cell_micro" "always") + (set_attr "length" "8")]) + +;; Move up to 4 bytes at a time. +(define_expand "movmemsi_1reg" + [(parallel [(set (match_operand 0 "" "") + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (use (match_operand 3 "" "")) + (clobber (match_scratch:SI 4 "")) + (clobber (match_scratch:SI 5 ""))])] + "TARGET_STRING" + "") + +(define_insn "" + [(set (mem:BLK (match_operand:P 0 "gpc_reg_operand" "b")) + (mem:BLK (match_operand:P 1 "gpc_reg_operand" "b"))) + (use (match_operand:SI 2 "immediate_operand" "i")) + (use (match_operand:SI 3 "immediate_operand" "i")) + (clobber (match_scratch:SI 4 "=&r")) + (clobber (match_scratch:SI 5 "=X"))] + "TARGET_STRING && INTVAL (operands[2]) > 0 && INTVAL (operands[2]) <= 4" + "lswi %4,%1,%2\;stswi %4,%0,%2" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "cell_micro" "always") + (set_attr "length" "8")]) + +;; Define insns that do load or store with update. Some of these we can +;; get by using pre-decrement or pre-increment, but the hardware can also +;; do cases where the increment is not the size of the object. +;; +;; In all these cases, we use operands 0 and 1 for the register being +;; incremented because those are the operands that local-alloc will +;; tie and these are the pair most likely to be tieable (and the ones +;; that will benefit the most). + +(define_insn "*movdi_update1" + [(set (match_operand:DI 3 "gpc_reg_operand" "=r,r") + (mem:DI (plus:DI (match_operand:DI 1 "gpc_reg_operand" "0,0") + (match_operand:DI 2 "reg_or_aligned_short_operand" "r,I")))) + (set (match_operand:DI 0 "gpc_reg_operand" "=b,b") + (plus:DI (match_dup 1) (match_dup 2)))] + "TARGET_POWERPC64 && TARGET_UPDATE + && (!avoiding_indexed_address_p (DImode) + || !gpc_reg_operand (operands[2], DImode))" + "@ + ldux %3,%0,%2 + ldu %3,%2(%0)" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "movdi_<mode>_update" + [(set (mem:DI (plus:P (match_operand:P 1 "gpc_reg_operand" "0,0") + (match_operand:P 2 "reg_or_aligned_short_operand" "r,I"))) + (match_operand:DI 3 "gpc_reg_operand" "r,r")) + (set (match_operand:P 0 "gpc_reg_operand" "=b,b") + (plus:P (match_dup 1) (match_dup 2)))] + "TARGET_POWERPC64 && TARGET_UPDATE + && (!avoiding_indexed_address_p (Pmode) + || !gpc_reg_operand (operands[2], Pmode) + || (REG_P (operands[0]) + && REGNO (operands[0]) == STACK_POINTER_REGNUM))" + "@ + stdux %3,%0,%2 + stdu %3,%2(%0)" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +;; This pattern is only conditional on TARGET_POWERPC64, as it is +;; needed for stack allocation, even if the user passes -mno-update. +(define_insn "movdi_<mode>_update_stack" + [(set (mem:DI (plus:P (match_operand:P 1 "gpc_reg_operand" "0,0") + (match_operand:P 2 "reg_or_aligned_short_operand" "r,I"))) + (match_operand:DI 3 "gpc_reg_operand" "r,r")) + (set (match_operand:P 0 "gpc_reg_operand" "=b,b") + (plus:P (match_dup 1) (match_dup 2)))] + "TARGET_POWERPC64" + "@ + stdux %3,%0,%2 + stdu %3,%2(%0)" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movsi_update1" + [(set (match_operand:SI 3 "gpc_reg_operand" "=r,r") + (mem:SI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I")))) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + lwzux %3,%0,%2 + lwzu %3,%2(%0)" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movsi_update2" + [(set (match_operand:DI 3 "gpc_reg_operand" "=r") + (sign_extend:DI + (mem:SI (plus:DI (match_operand:DI 1 "gpc_reg_operand" "0") + (match_operand:DI 2 "gpc_reg_operand" "r"))))) + (set (match_operand:DI 0 "gpc_reg_operand" "=b") + (plus:DI (match_dup 1) (match_dup 2)))] + "TARGET_POWERPC64 && rs6000_gen_cell_microcode + && !avoiding_indexed_address_p (DImode)" + "lwaux %3,%0,%2" + [(set_attr "type" "load") + (set_attr "sign_extend" "yes") + (set_attr "update" "yes") + (set_attr "indexed" "yes")]) + +(define_insn "movsi_update" + [(set (mem:SI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I"))) + (match_operand:SI 3 "gpc_reg_operand" "r,r")) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode) + || (REG_P (operands[0]) + && REGNO (operands[0]) == STACK_POINTER_REGNUM))" + "@ + stwux %3,%0,%2 + stwu %3,%2(%0)" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +;; This is an unconditional pattern; needed for stack allocation, even +;; if the user passes -mno-update. +(define_insn "movsi_update_stack" + [(set (mem:SI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I"))) + (match_operand:SI 3 "gpc_reg_operand" "r,r")) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "" + "@ + stwux %3,%0,%2 + stwu %3,%2(%0)" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movhi_update1" + [(set (match_operand:HI 3 "gpc_reg_operand" "=r,r") + (mem:HI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I")))) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + lhzux %3,%0,%2 + lhzu %3,%2(%0)" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movhi_update2" + [(set (match_operand:SI 3 "gpc_reg_operand" "=r,r") + (zero_extend:SI + (mem:HI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I"))))) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + lhzux %3,%0,%2 + lhzu %3,%2(%0)" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movhi_update3" + [(set (match_operand:SI 3 "gpc_reg_operand" "=r,r") + (sign_extend:SI + (mem:HI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I"))))) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_UPDATE && rs6000_gen_cell_microcode + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + lhaux %3,%0,%2 + lhau %3,%2(%0)" + [(set_attr "type" "load") + (set_attr "sign_extend" "yes") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movhi_update4" + [(set (mem:HI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I"))) + (match_operand:HI 3 "gpc_reg_operand" "r,r")) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + sthux %3,%0,%2 + sthu %3,%2(%0)" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movqi_update1" + [(set (match_operand:QI 3 "gpc_reg_operand" "=r,r") + (mem:QI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I")))) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + lbzux %3,%0,%2 + lbzu %3,%2(%0)" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movqi_update2" + [(set (match_operand:SI 3 "gpc_reg_operand" "=r,r") + (zero_extend:SI + (mem:QI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I"))))) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + lbzux %3,%0,%2 + lbzu %3,%2(%0)" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movqi_update3" + [(set (mem:QI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I"))) + (match_operand:QI 3 "gpc_reg_operand" "r,r")) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + stbux %3,%0,%2 + stbu %3,%2(%0)" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movsf_update1" + [(set (match_operand:SF 3 "gpc_reg_operand" "=f,f") + (mem:SF (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I")))) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT && TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + lfsux %3,%0,%2 + lfsu %3,%2(%0)" + [(set_attr "type" "fpload") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movsf_update2" + [(set (mem:SF (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I"))) + (match_operand:SF 3 "gpc_reg_operand" "f,f")) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_SINGLE_FLOAT && TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + stfsux %3,%0,%2 + stfsu %3,%2(%0)" + [(set_attr "type" "fpstore") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movsf_update3" + [(set (match_operand:SF 3 "gpc_reg_operand" "=r,r") + (mem:SF (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I")))) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "(TARGET_SOFT_FLOAT || !TARGET_FPRS) && TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + lwzux %3,%0,%2 + lwzu %3,%2(%0)" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movsf_update4" + [(set (mem:SF (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I"))) + (match_operand:SF 3 "gpc_reg_operand" "r,r")) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "(TARGET_SOFT_FLOAT || !TARGET_FPRS) && TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + stwux %3,%0,%2 + stwu %3,%2(%0)" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + +(define_insn "*movdf_update1" + [(set (match_operand:DF 3 "gpc_reg_operand" "=d,d") + (mem:DF (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I")))) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + lfdux %3,%0,%2 + lfdu %3,%2(%0)" + [(set_attr "type" "fpload") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no") + (set_attr "size" "64")]) + +(define_insn "*movdf_update2" + [(set (mem:DF (plus:SI (match_operand:SI 1 "gpc_reg_operand" "0,0") + (match_operand:SI 2 "reg_or_short_operand" "r,I"))) + (match_operand:DF 3 "gpc_reg_operand" "d,d")) + (set (match_operand:SI 0 "gpc_reg_operand" "=b,b") + (plus:SI (match_dup 1) (match_dup 2)))] + "TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_UPDATE + && (!avoiding_indexed_address_p (SImode) + || !gpc_reg_operand (operands[2], SImode))" + "@ + stfdux %3,%0,%2 + stfdu %3,%2(%0)" + [(set_attr "type" "fpstore") + (set_attr "update" "yes") + (set_attr "indexed" "yes,no")]) + + +;; After inserting conditional returns we can sometimes have +;; unnecessary register moves. Unfortunately we cannot have a +;; modeless peephole here, because some single SImode sets have early +;; clobber outputs. Although those sets expand to multi-ppc-insn +;; sequences, using get_attr_length here will smash the operands +;; array. Neither is there an early_cobbler_p predicate. +;; Disallow subregs for E500 so we don't munge frob_di_df_2. +;; Also this optimization interferes with scalars going into +;; altivec registers (the code does reloading through the FPRs). +(define_peephole2 + [(set (match_operand:DF 0 "gpc_reg_operand" "") + (match_operand:DF 1 "any_operand" "")) + (set (match_operand:DF 2 "gpc_reg_operand" "") + (match_dup 0))] + "!(TARGET_E500_DOUBLE && GET_CODE (operands[2]) == SUBREG) + && !TARGET_UPPER_REGS_DF + && peep2_reg_dead_p (2, operands[0])" + [(set (match_dup 2) (match_dup 1))]) + +(define_peephole2 + [(set (match_operand:SF 0 "gpc_reg_operand" "") + (match_operand:SF 1 "any_operand" "")) + (set (match_operand:SF 2 "gpc_reg_operand" "") + (match_dup 0))] + "!TARGET_UPPER_REGS_SF + && peep2_reg_dead_p (2, operands[0])" + [(set (match_dup 2) (match_dup 1))]) + + +;; TLS support. + +;; Mode attributes for different ABIs. +(define_mode_iterator TLSmode [(SI "! TARGET_64BIT") (DI "TARGET_64BIT")]) +(define_mode_attr tls_abi_suffix [(SI "32") (DI "64")]) +(define_mode_attr tls_sysv_suffix [(SI "si") (DI "di")]) +(define_mode_attr tls_insn_suffix [(SI "wz") (DI "d")]) + +(define_insn_and_split "tls_gd_aix<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (call (mem:TLSmode (match_operand:TLSmode 3 "symbol_ref_operand" "s")) + (match_operand 4 "" "g"))) + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGD) + (clobber (reg:SI LR_REGNO))] + "HAVE_AS_TLS && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)" +{ + if (TARGET_CMODEL != CMODEL_SMALL) + return "addis %0,%1,%2@got@tlsgd@ha\;addi %0,%0,%2@got@tlsgd@l\;" + "bl %z3\;nop"; + else + return "addi %0,%1,%2@got@tlsgd\;bl %z3\;nop"; +} + "&& TARGET_TLS_MARKERS" + [(set (match_dup 0) + (unspec:TLSmode [(match_dup 1) + (match_dup 2)] + UNSPEC_TLSGD)) + (parallel [(set (match_dup 0) + (call (mem:TLSmode (match_dup 3)) + (match_dup 4))) + (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGD) + (clobber (reg:SI LR_REGNO))])] + "" + [(set_attr "type" "two") + (set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL")) + (const_int 16) + (const_int 12)))]) + +(define_insn_and_split "tls_gd_sysv<TLSmode:tls_sysv_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (call (mem:TLSmode (match_operand:TLSmode 3 "symbol_ref_operand" "s")) + (match_operand 4 "" "g"))) + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGD) + (clobber (reg:SI LR_REGNO))] + "HAVE_AS_TLS && DEFAULT_ABI == ABI_V4" +{ + if (flag_pic) + { + if (TARGET_SECURE_PLT && flag_pic == 2) + return "addi %0,%1,%2@got@tlsgd\;bl %z3+32768@plt"; + else + return "addi %0,%1,%2@got@tlsgd\;bl %z3@plt"; + } + else + return "addi %0,%1,%2@got@tlsgd\;bl %z3"; +} + "&& TARGET_TLS_MARKERS" + [(set (match_dup 0) + (unspec:TLSmode [(match_dup 1) + (match_dup 2)] + UNSPEC_TLSGD)) + (parallel [(set (match_dup 0) + (call (mem:TLSmode (match_dup 3)) + (match_dup 4))) + (unspec:TLSmode [(match_dup 2)] UNSPEC_TLSGD) + (clobber (reg:SI LR_REGNO))])] + "" + [(set_attr "type" "two") + (set_attr "length" "8")]) + +(define_insn_and_split "*tls_gd<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGD))] + "HAVE_AS_TLS && TARGET_TLS_MARKERS" + "addi %0,%1,%2@got@tlsgd" + "&& TARGET_CMODEL != CMODEL_SMALL" + [(set (match_dup 3) + (high:TLSmode + (unspec:TLSmode [(match_dup 1) (match_dup 2)] UNSPEC_TLSGD))) + (set (match_dup 0) + (lo_sum:TLSmode (match_dup 3) + (unspec:TLSmode [(match_dup 1) (match_dup 2)] UNSPEC_TLSGD)))] + " +{ + operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode); +}" + [(set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL")) + (const_int 8) + (const_int 4)))]) + +(define_insn "*tls_gd_high<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (high:TLSmode + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGD)))] + "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL" + "addis %0,%1,%2@got@tlsgd@ha" + [(set_attr "length" "4")]) + +(define_insn "*tls_gd_low<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b") + (unspec:TLSmode [(match_operand:TLSmode 3 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGD)))] + "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL" + "addi %0,%1,%2@got@tlsgd@l" + [(set_attr "length" "4")]) + +(define_insn "*tls_gd_call_aix<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (call (mem:TLSmode (match_operand:TLSmode 1 "symbol_ref_operand" "s")) + (match_operand 2 "" "g"))) + (unspec:TLSmode [(match_operand:TLSmode 3 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGD) + (clobber (reg:SI LR_REGNO))] + "HAVE_AS_TLS && TARGET_TLS_MARKERS + && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)" + "bl %z1(%3@tlsgd)\;nop" + [(set_attr "type" "branch") + (set_attr "length" "8")]) + +(define_insn "*tls_gd_call_sysv<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (call (mem:TLSmode (match_operand:TLSmode 1 "symbol_ref_operand" "s")) + (match_operand 2 "" "g"))) + (unspec:TLSmode [(match_operand:TLSmode 3 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGD) + (clobber (reg:SI LR_REGNO))] + "HAVE_AS_TLS && DEFAULT_ABI == ABI_V4 && TARGET_TLS_MARKERS" +{ + if (flag_pic) + { + if (TARGET_SECURE_PLT && flag_pic == 2) + return "bl %z1+32768(%3@tlsgd)@plt"; + return "bl %z1(%3@tlsgd)@plt"; + } + return "bl %z1(%3@tlsgd)"; +} + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn_and_split "tls_ld_aix<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (call (mem:TLSmode (match_operand:TLSmode 2 "symbol_ref_operand" "s")) + (match_operand 3 "" "g"))) + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")] + UNSPEC_TLSLD) + (clobber (reg:SI LR_REGNO))] + "HAVE_AS_TLS && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)" +{ + if (TARGET_CMODEL != CMODEL_SMALL) + return "addis %0,%1,%&@got@tlsld@ha\;addi %0,%0,%&@got@tlsld@l\;" + "bl %z2\;nop"; + else + return "addi %0,%1,%&@got@tlsld\;bl %z2\;nop"; +} + "&& TARGET_TLS_MARKERS" + [(set (match_dup 0) + (unspec:TLSmode [(match_dup 1)] + UNSPEC_TLSLD)) + (parallel [(set (match_dup 0) + (call (mem:TLSmode (match_dup 2)) + (match_dup 3))) + (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD) + (clobber (reg:SI LR_REGNO))])] + "" + [(set_attr "type" "two") + (set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL")) + (const_int 16) + (const_int 12)))]) + +(define_insn_and_split "tls_ld_sysv<TLSmode:tls_sysv_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (call (mem:TLSmode (match_operand:TLSmode 2 "symbol_ref_operand" "s")) + (match_operand 3 "" "g"))) + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")] + UNSPEC_TLSLD) + (clobber (reg:SI LR_REGNO))] + "HAVE_AS_TLS && DEFAULT_ABI == ABI_V4" +{ + if (flag_pic) + { + if (TARGET_SECURE_PLT && flag_pic == 2) + return "addi %0,%1,%&@got@tlsld\;bl %z2+32768@plt"; + else + return "addi %0,%1,%&@got@tlsld\;bl %z2@plt"; + } + else + return "addi %0,%1,%&@got@tlsld\;bl %z2"; +} + "&& TARGET_TLS_MARKERS" + [(set (match_dup 0) + (unspec:TLSmode [(match_dup 1)] + UNSPEC_TLSLD)) + (parallel [(set (match_dup 0) + (call (mem:TLSmode (match_dup 2)) + (match_dup 3))) + (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD) + (clobber (reg:SI LR_REGNO))])] + "" + [(set_attr "length" "8")]) + +(define_insn_and_split "*tls_ld<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b")] + UNSPEC_TLSLD))] + "HAVE_AS_TLS && TARGET_TLS_MARKERS" + "addi %0,%1,%&@got@tlsld" + "&& TARGET_CMODEL != CMODEL_SMALL" + [(set (match_dup 2) + (high:TLSmode + (unspec:TLSmode [(const_int 0) (match_dup 1)] UNSPEC_TLSLD))) + (set (match_dup 0) + (lo_sum:TLSmode (match_dup 2) + (unspec:TLSmode [(const_int 0) (match_dup 1)] UNSPEC_TLSLD)))] + " +{ + operands[2] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode); +}" + [(set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL")) + (const_int 8) + (const_int 4)))]) + +(define_insn "*tls_ld_high<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (high:TLSmode + (unspec:TLSmode [(const_int 0) + (match_operand:TLSmode 1 "gpc_reg_operand" "b")] + UNSPEC_TLSLD)))] + "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL" + "addis %0,%1,%&@got@tlsld@ha" + [(set_attr "length" "4")]) + +(define_insn "*tls_ld_low<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b") + (unspec:TLSmode [(const_int 0) + (match_operand:TLSmode 2 "gpc_reg_operand" "b")] + UNSPEC_TLSLD)))] + "HAVE_AS_TLS && TARGET_TLS_MARKERS && TARGET_CMODEL != CMODEL_SMALL" + "addi %0,%1,%&@got@tlsld@l" + [(set_attr "length" "4")]) + +(define_insn "*tls_ld_call_aix<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (call (mem:TLSmode (match_operand:TLSmode 1 "symbol_ref_operand" "s")) + (match_operand 2 "" "g"))) + (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD) + (clobber (reg:SI LR_REGNO))] + "HAVE_AS_TLS && TARGET_TLS_MARKERS + && (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2)" + "bl %z1(%&@tlsld)\;nop" + [(set_attr "type" "branch") + (set_attr "length" "8")]) + +(define_insn "*tls_ld_call_sysv<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (call (mem:TLSmode (match_operand:TLSmode 1 "symbol_ref_operand" "s")) + (match_operand 2 "" "g"))) + (unspec:TLSmode [(const_int 0)] UNSPEC_TLSLD) + (clobber (reg:SI LR_REGNO))] + "HAVE_AS_TLS && DEFAULT_ABI == ABI_V4 && TARGET_TLS_MARKERS" +{ + if (flag_pic) + { + if (TARGET_SECURE_PLT && flag_pic == 2) + return "bl %z1+32768(%&@tlsld)@plt"; + return "bl %z1(%&@tlsld)@plt"; + } + return "bl %z1(%&@tlsld)"; +} + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "tls_dtprel_<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSDTPREL))] + "HAVE_AS_TLS" + "addi %0,%1,%2@dtprel") + +(define_insn "tls_dtprel_ha_<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSDTPRELHA))] + "HAVE_AS_TLS" + "addis %0,%1,%2@dtprel@ha") + +(define_insn "tls_dtprel_lo_<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSDTPRELLO))] + "HAVE_AS_TLS" + "addi %0,%1,%2@dtprel@l") + +(define_insn_and_split "tls_got_dtprel_<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGOTDTPREL))] + "HAVE_AS_TLS" + "l<TLSmode:tls_insn_suffix> %0,%2@got@dtprel(%1)" + "&& TARGET_CMODEL != CMODEL_SMALL" + [(set (match_dup 3) + (high:TLSmode + (unspec:TLSmode [(match_dup 1) (match_dup 2)] UNSPEC_TLSGOTDTPREL))) + (set (match_dup 0) + (lo_sum:TLSmode (match_dup 3) + (unspec:TLSmode [(match_dup 1) (match_dup 2)] UNSPEC_TLSGOTDTPREL)))] + " +{ + operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode); +}" + [(set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL")) + (const_int 8) + (const_int 4)))]) + +(define_insn "*tls_got_dtprel_high<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (high:TLSmode + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGOTDTPREL)))] + "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL" + "addis %0,%1,%2@got@dtprel@ha" + [(set_attr "length" "4")]) + +(define_insn "*tls_got_dtprel_low<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") + (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b") + (unspec:TLSmode [(match_operand:TLSmode 3 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGOTDTPREL)))] + "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL" + "l<TLSmode:tls_insn_suffix> %0,%2@got@dtprel@l(%1)" + [(set_attr "length" "4")]) + +(define_insn "tls_tprel_<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSTPREL))] + "HAVE_AS_TLS" + "addi %0,%1,%2@tprel") + +(define_insn "tls_tprel_ha_<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSTPRELHA))] + "HAVE_AS_TLS" + "addis %0,%1,%2@tprel@ha") + +(define_insn "tls_tprel_lo_<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSTPRELLO))] + "HAVE_AS_TLS" + "addi %0,%1,%2@tprel@l") + +;; "b" output constraint here and on tls_tls input to support linker tls +;; optimization. The linker may edit the instructions emitted by a +;; tls_got_tprel/tls_tls pair to addis,addi. +(define_insn_and_split "tls_got_tprel_<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGOTTPREL))] + "HAVE_AS_TLS" + "l<TLSmode:tls_insn_suffix> %0,%2@got@tprel(%1)" + "&& TARGET_CMODEL != CMODEL_SMALL" + [(set (match_dup 3) + (high:TLSmode + (unspec:TLSmode [(match_dup 1) (match_dup 2)] UNSPEC_TLSGOTTPREL))) + (set (match_dup 0) + (lo_sum:TLSmode (match_dup 3) + (unspec:TLSmode [(match_dup 1) (match_dup 2)] UNSPEC_TLSGOTTPREL)))] + " +{ + operands[3] = gen_reg_rtx (TARGET_64BIT ? DImode : SImode); +}" + [(set (attr "length") + (if_then_else (ne (symbol_ref "TARGET_CMODEL") (symbol_ref "CMODEL_SMALL")) + (const_int 8) + (const_int 4)))]) + +(define_insn "*tls_got_tprel_high<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=b") + (high:TLSmode + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGOTTPREL)))] + "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL" + "addis %0,%1,%2@got@tprel@ha" + [(set_attr "length" "4")]) + +(define_insn "*tls_got_tprel_low<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") + (lo_sum:TLSmode (match_operand:TLSmode 1 "gpc_reg_operand" "b") + (unspec:TLSmode [(match_operand:TLSmode 3 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSGOTTPREL)))] + "HAVE_AS_TLS && TARGET_CMODEL != CMODEL_SMALL" + "l<TLSmode:tls_insn_suffix> %0,%2@got@tprel@l(%1)" + [(set_attr "length" "4")]) + +(define_insn "tls_tls_<TLSmode:tls_abi_suffix>" + [(set (match_operand:TLSmode 0 "gpc_reg_operand" "=r") + (unspec:TLSmode [(match_operand:TLSmode 1 "gpc_reg_operand" "b") + (match_operand:TLSmode 2 "rs6000_tls_symbol_ref" "")] + UNSPEC_TLSTLS))] + "TARGET_ELF && HAVE_AS_TLS" + "add %0,%1,%2@tls") + +(define_expand "tls_get_tpointer" + [(set (match_operand:SI 0 "gpc_reg_operand" "") + (unspec:SI [(const_int 0)] UNSPEC_TLSTLS))] + "TARGET_XCOFF && HAVE_AS_TLS" + " +{ + emit_insn (gen_tls_get_tpointer_internal ()); + emit_move_insn (operands[0], gen_rtx_REG (SImode, 3)); + DONE; +}") + +(define_insn "tls_get_tpointer_internal" + [(set (reg:SI 3) + (unspec:SI [(const_int 0)] UNSPEC_TLSTLS)) + (clobber (reg:SI LR_REGNO))] + "TARGET_XCOFF && HAVE_AS_TLS" + "bla __get_tpointer") + +(define_expand "tls_get_addr<mode>" + [(set (match_operand:P 0 "gpc_reg_operand" "") + (unspec:P [(match_operand:P 1 "gpc_reg_operand" "") + (match_operand:P 2 "gpc_reg_operand" "")] UNSPEC_TLSTLS))] + "TARGET_XCOFF && HAVE_AS_TLS" + " +{ + emit_move_insn (gen_rtx_REG (Pmode, 3), operands[1]); + emit_move_insn (gen_rtx_REG (Pmode, 4), operands[2]); + emit_insn (gen_tls_get_addr_internal<mode> ()); + emit_move_insn (operands[0], gen_rtx_REG (Pmode, 3)); + DONE; +}") + +(define_insn "tls_get_addr_internal<mode>" + [(set (reg:P 3) + (unspec:P [(reg:P 3) (reg:P 4)] UNSPEC_TLSTLS)) + (clobber (reg:P 0)) + (clobber (reg:P 4)) + (clobber (reg:P 5)) + (clobber (reg:P 11)) + (clobber (reg:CC CR0_REGNO)) + (clobber (reg:P LR_REGNO))] + "TARGET_XCOFF && HAVE_AS_TLS" + "bla __tls_get_addr") + +;; Next come insns related to the calling sequence. +;; +;; First, an insn to allocate new stack space for dynamic use (e.g., alloca). +;; We move the back-chain and decrement the stack pointer. + +(define_expand "allocate_stack" + [(set (match_operand 0 "gpc_reg_operand" "") + (minus (reg 1) (match_operand 1 "reg_or_short_operand" ""))) + (set (reg 1) + (minus (reg 1) (match_dup 1)))] + "" + " +{ rtx chain = gen_reg_rtx (Pmode); + rtx stack_bot = gen_rtx_MEM (Pmode, stack_pointer_rtx); + rtx neg_op0; + rtx insn, par, set, mem; + + emit_move_insn (chain, stack_bot); + + /* Check stack bounds if necessary. */ + if (crtl->limit_stack) + { + rtx available; + available = expand_binop (Pmode, sub_optab, + stack_pointer_rtx, stack_limit_rtx, + NULL_RTX, 1, OPTAB_WIDEN); + emit_insn (gen_cond_trap (LTU, available, operands[1], const0_rtx)); + } + + if (GET_CODE (operands[1]) != CONST_INT + || INTVAL (operands[1]) < -32767 + || INTVAL (operands[1]) > 32768) + { + neg_op0 = gen_reg_rtx (Pmode); + if (TARGET_32BIT) + emit_insn (gen_negsi2 (neg_op0, operands[1])); + else + emit_insn (gen_negdi2 (neg_op0, operands[1])); + } + else + neg_op0 = GEN_INT (- INTVAL (operands[1])); + + insn = emit_insn ((* ((TARGET_32BIT) ? gen_movsi_update_stack + : gen_movdi_di_update_stack)) + (stack_pointer_rtx, stack_pointer_rtx, neg_op0, + chain)); + /* Since we didn't use gen_frame_mem to generate the MEM, grab + it now and set the alias set/attributes. The above gen_*_update + calls will generate a PARALLEL with the MEM set being the first + operation. */ + par = PATTERN (insn); + gcc_assert (GET_CODE (par) == PARALLEL); + set = XVECEXP (par, 0, 0); + gcc_assert (GET_CODE (set) == SET); + mem = SET_DEST (set); + gcc_assert (MEM_P (mem)); + MEM_NOTRAP_P (mem) = 1; + set_mem_alias_set (mem, get_frame_alias_set ()); + + emit_move_insn (operands[0], virtual_stack_dynamic_rtx); + DONE; +}") + +;; These patterns say how to save and restore the stack pointer. We need not +;; save the stack pointer at function level since we are careful to +;; preserve the backchain. At block level, we have to restore the backchain +;; when we restore the stack pointer. +;; +;; For nonlocal gotos, we must save both the stack pointer and its +;; backchain and restore both. Note that in the nonlocal case, the +;; save area is a memory location. + +(define_expand "save_stack_function" + [(match_operand 0 "any_operand" "") + (match_operand 1 "any_operand" "")] + "" + "DONE;") + +(define_expand "restore_stack_function" + [(match_operand 0 "any_operand" "") + (match_operand 1 "any_operand" "")] + "" + "DONE;") + +;; Adjust stack pointer (op0) to a new value (op1). +;; First copy old stack backchain to new location, and ensure that the +;; scheduler won't reorder the sp assignment before the backchain write. +(define_expand "restore_stack_block" + [(set (match_dup 2) (match_dup 3)) + (set (match_dup 4) (match_dup 2)) + (match_dup 5) + (set (match_operand 0 "register_operand" "") + (match_operand 1 "register_operand" ""))] + "" + " +{ + rtvec p; + + operands[1] = force_reg (Pmode, operands[1]); + operands[2] = gen_reg_rtx (Pmode); + operands[3] = gen_frame_mem (Pmode, operands[0]); + operands[4] = gen_frame_mem (Pmode, operands[1]); + p = rtvec_alloc (1); + RTVEC_ELT (p, 0) = gen_rtx_SET (gen_frame_mem (BLKmode, operands[0]), + const0_rtx); + operands[5] = gen_rtx_PARALLEL (VOIDmode, p); +}") + +(define_expand "save_stack_nonlocal" + [(set (match_dup 3) (match_dup 4)) + (set (match_operand 0 "memory_operand" "") (match_dup 3)) + (set (match_dup 2) (match_operand 1 "register_operand" ""))] + "" + " +{ + int units_per_word = (TARGET_32BIT) ? 4 : 8; + + /* Copy the backchain to the first word, sp to the second. */ + operands[0] = adjust_address_nv (operands[0], Pmode, 0); + operands[2] = adjust_address_nv (operands[0], Pmode, units_per_word); + operands[3] = gen_reg_rtx (Pmode); + operands[4] = gen_frame_mem (Pmode, operands[1]); +}") + +(define_expand "restore_stack_nonlocal" + [(set (match_dup 2) (match_operand 1 "memory_operand" "")) + (set (match_dup 3) (match_dup 4)) + (set (match_dup 5) (match_dup 2)) + (match_dup 6) + (set (match_operand 0 "register_operand" "") (match_dup 3))] + "" + " +{ + int units_per_word = (TARGET_32BIT) ? 4 : 8; + rtvec p; + + /* Restore the backchain from the first word, sp from the second. */ + operands[2] = gen_reg_rtx (Pmode); + operands[3] = gen_reg_rtx (Pmode); + operands[1] = adjust_address_nv (operands[1], Pmode, 0); + operands[4] = adjust_address_nv (operands[1], Pmode, units_per_word); + operands[5] = gen_frame_mem (Pmode, operands[3]); + p = rtvec_alloc (1); + RTVEC_ELT (p, 0) = gen_rtx_SET (gen_frame_mem (BLKmode, operands[0]), + const0_rtx); + operands[6] = gen_rtx_PARALLEL (VOIDmode, p); +}") + +;; TOC register handling. + +;; Code to initialize the TOC register... + +(define_insn "load_toc_aix_si" + [(parallel [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (unspec:SI [(const_int 0)] UNSPEC_TOC)) + (use (reg:SI 2))])] + "(DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2) && TARGET_32BIT" + "* +{ + char buf[30]; + extern int need_toc_init; + need_toc_init = 1; + ASM_GENERATE_INTERNAL_LABEL (buf, \"LCTOC\", 1); + operands[1] = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); + operands[2] = gen_rtx_REG (Pmode, 2); + return \"lwz %0,%1(%2)\"; +}" + [(set_attr "type" "load") + (set_attr "update" "no") + (set_attr "indexed" "no")]) + +(define_insn "load_toc_aix_di" + [(parallel [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (unspec:DI [(const_int 0)] UNSPEC_TOC)) + (use (reg:DI 2))])] + "(DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2) && TARGET_64BIT" + "* +{ + char buf[30]; + extern int need_toc_init; + need_toc_init = 1; + ASM_GENERATE_INTERNAL_LABEL (buf, \"LCTOC\", + !TARGET_ELF || !TARGET_MINIMAL_TOC); + if (TARGET_ELF) + strcat (buf, \"@toc\"); + operands[1] = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf)); + operands[2] = gen_rtx_REG (Pmode, 2); + return \"ld %0,%1(%2)\"; +}" + [(set_attr "type" "load") + (set_attr "update" "no") + (set_attr "indexed" "no")]) + +(define_insn "load_toc_v4_pic_si" + [(set (reg:SI LR_REGNO) + (unspec:SI [(const_int 0)] UNSPEC_TOC))] + "DEFAULT_ABI == ABI_V4 && flag_pic == 1 && TARGET_32BIT" + "bl _GLOBAL_OFFSET_TABLE_@local-4" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_expand "load_toc_v4_PIC_1" + [(parallel [(set (reg:SI LR_REGNO) + (match_operand:SI 0 "immediate_operand" "s")) + (use (unspec [(match_dup 0)] UNSPEC_TOC))])] + "TARGET_ELF && DEFAULT_ABI == ABI_V4 + && (flag_pic == 2 || (flag_pic && TARGET_SECURE_PLT))" + "") + +(define_insn "load_toc_v4_PIC_1_normal" + [(set (reg:SI LR_REGNO) + (match_operand:SI 0 "immediate_operand" "s")) + (use (unspec [(match_dup 0)] UNSPEC_TOC))] + "!TARGET_LINK_STACK && TARGET_ELF && DEFAULT_ABI == ABI_V4 + && (flag_pic == 2 || (flag_pic && TARGET_SECURE_PLT))" + "bcl 20,31,%0\\n%0:" + [(set_attr "type" "branch") + (set_attr "length" "4") + (set_attr "cannot_copy" "yes")]) + +(define_insn "load_toc_v4_PIC_1_476" + [(set (reg:SI LR_REGNO) + (match_operand:SI 0 "immediate_operand" "s")) + (use (unspec [(match_dup 0)] UNSPEC_TOC))] + "TARGET_LINK_STACK && TARGET_ELF && DEFAULT_ABI == ABI_V4 + && (flag_pic == 2 || (flag_pic && TARGET_SECURE_PLT))" + "* +{ + char name[32]; + static char templ[32]; + + get_ppc476_thunk_name (name); + sprintf (templ, \"bl %s\\n%%0:\", name); + return templ; +}" + [(set_attr "type" "branch") + (set_attr "length" "4") + (set_attr "cannot_copy" "yes")]) + +(define_expand "load_toc_v4_PIC_1b" + [(parallel [(set (reg:SI LR_REGNO) + (unspec:SI [(match_operand:SI 0 "immediate_operand" "s") + (label_ref (match_operand 1 "" ""))] + UNSPEC_TOCPTR)) + (match_dup 1)])] + "TARGET_ELF && DEFAULT_ABI == ABI_V4 && flag_pic == 2" + "") + +(define_insn "load_toc_v4_PIC_1b_normal" + [(set (reg:SI LR_REGNO) + (unspec:SI [(match_operand:SI 0 "immediate_operand" "s") + (label_ref (match_operand 1 "" ""))] + UNSPEC_TOCPTR)) + (match_dup 1)] + "!TARGET_LINK_STACK && TARGET_ELF && DEFAULT_ABI == ABI_V4 && flag_pic == 2" + "bcl 20,31,$+8\;.long %0-$" + [(set_attr "type" "branch") + (set_attr "length" "8")]) + +(define_insn "load_toc_v4_PIC_1b_476" + [(set (reg:SI LR_REGNO) + (unspec:SI [(match_operand:SI 0 "immediate_operand" "s") + (label_ref (match_operand 1 "" ""))] + UNSPEC_TOCPTR)) + (match_dup 1)] + "TARGET_LINK_STACK && TARGET_ELF && DEFAULT_ABI == ABI_V4 && flag_pic == 2" + "* +{ + char name[32]; + static char templ[32]; + + get_ppc476_thunk_name (name); + sprintf (templ, \"bl %s\\n\\tb $+8\\n\\t.long %%0-$\", name); + return templ; +}" + [(set_attr "type" "branch") + (set_attr "length" "16")]) + +(define_insn "load_toc_v4_PIC_2" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (mem:SI (plus:SI (match_operand:SI 1 "gpc_reg_operand" "b") + (minus:SI (match_operand:SI 2 "immediate_operand" "s") + (match_operand:SI 3 "immediate_operand" "s")))))] + "TARGET_ELF && DEFAULT_ABI == ABI_V4 && flag_pic == 2" + "lwz %0,%2-%3(%1)" + [(set_attr "type" "load")]) + +(define_insn "load_toc_v4_PIC_3b" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (match_operand:SI 1 "gpc_reg_operand" "b") + (high:SI + (minus:SI (match_operand:SI 2 "symbol_ref_operand" "s") + (match_operand:SI 3 "symbol_ref_operand" "s")))))] + "TARGET_ELF && TARGET_SECURE_PLT && DEFAULT_ABI == ABI_V4 && flag_pic" + "addis %0,%1,%2-%3@ha") + +(define_insn "load_toc_v4_PIC_3c" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (lo_sum:SI (match_operand:SI 1 "gpc_reg_operand" "b") + (minus:SI (match_operand:SI 2 "symbol_ref_operand" "s") + (match_operand:SI 3 "symbol_ref_operand" "s"))))] + "TARGET_ELF && TARGET_SECURE_PLT && DEFAULT_ABI == ABI_V4 && flag_pic" + "addi %0,%1,%2-%3@l") + +;; If the TOC is shared over a translation unit, as happens with all +;; the kinds of PIC that we support, we need to restore the TOC +;; pointer only when jumping over units of translation. +;; On Darwin, we need to reload the picbase. + +(define_expand "builtin_setjmp_receiver" + [(use (label_ref (match_operand 0 "" "")))] + "(DEFAULT_ABI == ABI_V4 && flag_pic == 1) + || (TARGET_TOC && TARGET_MINIMAL_TOC) + || (DEFAULT_ABI == ABI_DARWIN && flag_pic)" + " +{ +#if TARGET_MACHO + if (DEFAULT_ABI == ABI_DARWIN) + { + rtx picrtx = gen_rtx_SYMBOL_REF (Pmode, MACHOPIC_FUNCTION_BASE_NAME); + rtx picreg = gen_rtx_REG (Pmode, RS6000_PIC_OFFSET_TABLE_REGNUM); + rtx tmplabrtx; + char tmplab[20]; + + crtl->uses_pic_offset_table = 1; + ASM_GENERATE_INTERNAL_LABEL(tmplab, \"LSJR\", + CODE_LABEL_NUMBER (operands[0])); + tmplabrtx = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (tmplab)); + + emit_insn (gen_load_macho_picbase (tmplabrtx)); + emit_move_insn (picreg, gen_rtx_REG (Pmode, LR_REGNO)); + emit_insn (gen_macho_correct_pic (picreg, picreg, picrtx, tmplabrtx)); + } + else +#endif + rs6000_emit_load_toc_table (FALSE); + DONE; +}") + +;; Largetoc support +(define_insn "*largetoc_high" + [(set (match_operand:DI 0 "gpc_reg_operand" "=b*r") + (high:DI + (unspec [(match_operand:DI 1 "" "") + (match_operand:DI 2 "gpc_reg_operand" "b")] + UNSPEC_TOCREL)))] + "TARGET_ELF && TARGET_CMODEL != CMODEL_SMALL" + "addis %0,%2,%1@toc@ha") + +(define_insn "*largetoc_high_aix<mode>" + [(set (match_operand:P 0 "gpc_reg_operand" "=b*r") + (high:P + (unspec [(match_operand:P 1 "" "") + (match_operand:P 2 "gpc_reg_operand" "b")] + UNSPEC_TOCREL)))] + "TARGET_XCOFF && TARGET_CMODEL != CMODEL_SMALL" + "addis %0,%1@u(%2)") + +(define_insn "*largetoc_high_plus" + [(set (match_operand:DI 0 "gpc_reg_operand" "=b*r") + (high:DI + (plus:DI + (unspec [(match_operand:DI 1 "" "") + (match_operand:DI 2 "gpc_reg_operand" "b")] + UNSPEC_TOCREL) + (match_operand:DI 3 "add_cint_operand" "n"))))] + "TARGET_ELF && TARGET_CMODEL != CMODEL_SMALL" + "addis %0,%2,%1+%3@toc@ha") + +(define_insn "*largetoc_high_plus_aix<mode>" + [(set (match_operand:P 0 "gpc_reg_operand" "=b*r") + (high:P + (plus:P + (unspec [(match_operand:P 1 "" "") + (match_operand:P 2 "gpc_reg_operand" "b")] + UNSPEC_TOCREL) + (match_operand:P 3 "add_cint_operand" "n"))))] + "TARGET_XCOFF && TARGET_CMODEL != CMODEL_SMALL" + "addis %0,%1+%3@u(%2)") + +(define_insn "*largetoc_low" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (lo_sum:DI (match_operand:DI 1 "gpc_reg_operand" "b") + (match_operand:DI 2 "" "")))] + "TARGET_ELF && TARGET_CMODEL != CMODEL_SMALL" + "addi %0,%1,%2@l") + +(define_insn "*largetoc_low_aix<mode>" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (lo_sum:P (match_operand:P 1 "gpc_reg_operand" "b") + (match_operand:P 2 "" "")))] + "TARGET_XCOFF && TARGET_CMODEL != CMODEL_SMALL" + "la %0,%2@l(%1)") + +(define_insn_and_split "*tocref<mode>" + [(set (match_operand:P 0 "gpc_reg_operand" "=b") + (match_operand:P 1 "small_toc_ref" "R"))] + "TARGET_TOC" + "la %0,%a1" + "&& TARGET_CMODEL != CMODEL_SMALL && reload_completed" + [(set (match_dup 0) (high:P (match_dup 1))) + (set (match_dup 0) (lo_sum:P (match_dup 0) (match_dup 1)))]) + +;; Elf specific ways of loading addresses for non-PIC code. +;; The output of this could be r0, but we make a very strong +;; preference for a base register because it will usually +;; be needed there. +(define_insn "elf_high" + [(set (match_operand:SI 0 "gpc_reg_operand" "=b*r") + (high:SI (match_operand 1 "" "")))] + "TARGET_ELF && !TARGET_64BIT && !flag_pic" + "lis %0,%1@ha") + +(define_insn "elf_low" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (lo_sum:SI (match_operand:SI 1 "gpc_reg_operand" "b") + (match_operand 2 "" "")))] + "TARGET_ELF && !TARGET_64BIT && !flag_pic" + "la %0,%2@l(%1)") + +;; Call and call_value insns +(define_expand "call" + [(parallel [(call (mem:SI (match_operand 0 "address_operand" "")) + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (clobber (reg:SI LR_REGNO))])] + "" + " +{ +#if TARGET_MACHO + if (MACHOPIC_INDIRECT) + operands[0] = machopic_indirect_call_target (operands[0]); +#endif + + gcc_assert (GET_CODE (operands[0]) == MEM); + gcc_assert (GET_CODE (operands[1]) == CONST_INT); + + operands[0] = XEXP (operands[0], 0); + + if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2) + { + rs6000_call_aix (NULL_RTX, operands[0], operands[1], operands[2]); + DONE; + } + + if (GET_CODE (operands[0]) != SYMBOL_REF + || (DEFAULT_ABI != ABI_DARWIN && (INTVAL (operands[2]) & CALL_LONG) != 0)) + { + if (INTVAL (operands[2]) & CALL_LONG) + operands[0] = rs6000_longcall_ref (operands[0]); + + switch (DEFAULT_ABI) + { + case ABI_V4: + case ABI_DARWIN: + operands[0] = force_reg (Pmode, operands[0]); + break; + + default: + gcc_unreachable (); + } + } +}") + +(define_expand "call_value" + [(parallel [(set (match_operand 0 "" "") + (call (mem:SI (match_operand 1 "address_operand" "")) + (match_operand 2 "" ""))) + (use (match_operand 3 "" "")) + (clobber (reg:SI LR_REGNO))])] + "" + " +{ +#if TARGET_MACHO + if (MACHOPIC_INDIRECT) + operands[1] = machopic_indirect_call_target (operands[1]); +#endif + + gcc_assert (GET_CODE (operands[1]) == MEM); + gcc_assert (GET_CODE (operands[2]) == CONST_INT); + + operands[1] = XEXP (operands[1], 0); + + if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2) + { + rs6000_call_aix (operands[0], operands[1], operands[2], operands[3]); + DONE; + } + + if (GET_CODE (operands[1]) != SYMBOL_REF + || (DEFAULT_ABI != ABI_DARWIN && (INTVAL (operands[3]) & CALL_LONG) != 0)) + { + if (INTVAL (operands[3]) & CALL_LONG) + operands[1] = rs6000_longcall_ref (operands[1]); + + switch (DEFAULT_ABI) + { + case ABI_V4: + case ABI_DARWIN: + operands[1] = force_reg (Pmode, operands[1]); + break; + + default: + gcc_unreachable (); + } + } +}") + +;; Call to function in current module. No TOC pointer reload needed. +;; Operand2 is nonzero if we are using the V.4 calling sequence and +;; either the function was not prototyped, or it was prototyped as a +;; variable argument function. It is > 0 if FP registers were passed +;; and < 0 if they were not. + +(define_insn "*call_local32" + [(call (mem:SI (match_operand:SI 0 "current_file_function_operand" "s,s")) + (match_operand 1 "" "g,g")) + (use (match_operand:SI 2 "immediate_operand" "O,n")) + (clobber (reg:SI LR_REGNO))] + "(INTVAL (operands[2]) & CALL_LONG) == 0" + "* +{ + if (INTVAL (operands[2]) & CALL_V4_SET_FP_ARGS) + output_asm_insn (\"crxor 6,6,6\", operands); + + else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn (\"creqv 6,6,6\", operands); + + return (DEFAULT_ABI == ABI_V4 && flag_pic) ? \"bl %z0@local\" : \"bl %z0\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "4,8")]) + +(define_insn "*call_local64" + [(call (mem:SI (match_operand:DI 0 "current_file_function_operand" "s,s")) + (match_operand 1 "" "g,g")) + (use (match_operand:SI 2 "immediate_operand" "O,n")) + (clobber (reg:SI LR_REGNO))] + "TARGET_64BIT && (INTVAL (operands[2]) & CALL_LONG) == 0" + "* +{ + if (INTVAL (operands[2]) & CALL_V4_SET_FP_ARGS) + output_asm_insn (\"crxor 6,6,6\", operands); + + else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn (\"creqv 6,6,6\", operands); + + return (DEFAULT_ABI == ABI_V4 && flag_pic) ? \"bl %z0@local\" : \"bl %z0\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "4,8")]) + +(define_insn "*call_value_local32" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:SI 1 "current_file_function_operand" "s,s")) + (match_operand 2 "" "g,g"))) + (use (match_operand:SI 3 "immediate_operand" "O,n")) + (clobber (reg:SI LR_REGNO))] + "(INTVAL (operands[3]) & CALL_LONG) == 0" + "* +{ + if (INTVAL (operands[3]) & CALL_V4_SET_FP_ARGS) + output_asm_insn (\"crxor 6,6,6\", operands); + + else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn (\"creqv 6,6,6\", operands); + + return (DEFAULT_ABI == ABI_V4 && flag_pic) ? \"bl %z1@local\" : \"bl %z1\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "4,8")]) + + +(define_insn "*call_value_local64" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:DI 1 "current_file_function_operand" "s,s")) + (match_operand 2 "" "g,g"))) + (use (match_operand:SI 3 "immediate_operand" "O,n")) + (clobber (reg:SI LR_REGNO))] + "TARGET_64BIT && (INTVAL (operands[3]) & CALL_LONG) == 0" + "* +{ + if (INTVAL (operands[3]) & CALL_V4_SET_FP_ARGS) + output_asm_insn (\"crxor 6,6,6\", operands); + + else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn (\"creqv 6,6,6\", operands); + + return (DEFAULT_ABI == ABI_V4 && flag_pic) ? \"bl %z1@local\" : \"bl %z1\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "4,8")]) + + +;; A function pointer under System V is just a normal pointer +;; operands[0] is the function pointer +;; operands[1] is the stack size to clean up +;; operands[2] is the value FUNCTION_ARG returns for the VOID argument +;; which indicates how to set cr1 + +(define_insn "*call_indirect_nonlocal_sysv<mode>" + [(call (mem:SI (match_operand:P 0 "register_operand" "c,*l,c,*l")) + (match_operand 1 "" "g,g,g,g")) + (use (match_operand:SI 2 "immediate_operand" "O,O,n,n")) + (clobber (reg:SI LR_REGNO))] + "DEFAULT_ABI == ABI_V4 + || DEFAULT_ABI == ABI_DARWIN" +{ + if (INTVAL (operands[2]) & CALL_V4_SET_FP_ARGS) + output_asm_insn ("crxor 6,6,6", operands); + + else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn ("creqv 6,6,6", operands); + + return "b%T0l"; +} + [(set_attr "type" "jmpreg,jmpreg,jmpreg,jmpreg") + (set_attr "length" "4,4,8,8")]) + +(define_insn_and_split "*call_nonlocal_sysv<mode>" + [(call (mem:SI (match_operand:P 0 "symbol_ref_operand" "s,s")) + (match_operand 1 "" "g,g")) + (use (match_operand:SI 2 "immediate_operand" "O,n")) + (clobber (reg:SI LR_REGNO))] + "(DEFAULT_ABI == ABI_DARWIN + || (DEFAULT_ABI == ABI_V4 + && (INTVAL (operands[2]) & CALL_LONG) == 0))" +{ + if (INTVAL (operands[2]) & CALL_V4_SET_FP_ARGS) + output_asm_insn ("crxor 6,6,6", operands); + + else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn ("creqv 6,6,6", operands); + +#if TARGET_MACHO + return output_call(insn, operands, 0, 2); +#else + if (DEFAULT_ABI == ABI_V4 && flag_pic) + { + gcc_assert (!TARGET_SECURE_PLT); + return "bl %z0@plt"; + } + else + return "bl %z0"; +#endif +} + "DEFAULT_ABI == ABI_V4 + && TARGET_SECURE_PLT && flag_pic && !SYMBOL_REF_LOCAL_P (operands[0]) + && (INTVAL (operands[2]) & CALL_LONG) == 0" + [(parallel [(call (mem:SI (match_dup 0)) + (match_dup 1)) + (use (match_dup 2)) + (use (match_dup 3)) + (clobber (reg:SI LR_REGNO))])] +{ + operands[3] = pic_offset_table_rtx; +} + [(set_attr "type" "branch,branch") + (set_attr "length" "4,8")]) + +(define_insn "*call_nonlocal_sysv_secure<mode>" + [(call (mem:SI (match_operand:P 0 "symbol_ref_operand" "s,s")) + (match_operand 1 "" "g,g")) + (use (match_operand:SI 2 "immediate_operand" "O,n")) + (use (match_operand:SI 3 "register_operand" "r,r")) + (clobber (reg:SI LR_REGNO))] + "(DEFAULT_ABI == ABI_V4 + && TARGET_SECURE_PLT && flag_pic && !SYMBOL_REF_LOCAL_P (operands[0]) + && (INTVAL (operands[2]) & CALL_LONG) == 0)" +{ + if (INTVAL (operands[2]) & CALL_V4_SET_FP_ARGS) + output_asm_insn ("crxor 6,6,6", operands); + + else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn ("creqv 6,6,6", operands); + + if (flag_pic == 2) + /* The magic 32768 offset here and in the other sysv call insns + corresponds to the offset of r30 in .got2, as given by LCTOC1. + See sysv4.h:toc_section. */ + return "bl %z0+32768@plt"; + else + return "bl %z0@plt"; +} + [(set_attr "type" "branch,branch") + (set_attr "length" "4,8")]) + +(define_insn "*call_value_indirect_nonlocal_sysv<mode>" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:P 1 "register_operand" "c,*l,c,*l")) + (match_operand 2 "" "g,g,g,g"))) + (use (match_operand:SI 3 "immediate_operand" "O,O,n,n")) + (clobber (reg:SI LR_REGNO))] + "DEFAULT_ABI == ABI_V4 + || DEFAULT_ABI == ABI_DARWIN" +{ + if (INTVAL (operands[3]) & CALL_V4_SET_FP_ARGS) + output_asm_insn ("crxor 6,6,6", operands); + + else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn ("creqv 6,6,6", operands); + + return "b%T1l"; +} + [(set_attr "type" "jmpreg,jmpreg,jmpreg,jmpreg") + (set_attr "length" "4,4,8,8")]) + +(define_insn_and_split "*call_value_nonlocal_sysv<mode>" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s,s")) + (match_operand 2 "" "g,g"))) + (use (match_operand:SI 3 "immediate_operand" "O,n")) + (clobber (reg:SI LR_REGNO))] + "(DEFAULT_ABI == ABI_DARWIN + || (DEFAULT_ABI == ABI_V4 + && (INTVAL (operands[3]) & CALL_LONG) == 0))" +{ + if (INTVAL (operands[3]) & CALL_V4_SET_FP_ARGS) + output_asm_insn ("crxor 6,6,6", operands); + + else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn ("creqv 6,6,6", operands); + +#if TARGET_MACHO + return output_call(insn, operands, 1, 3); +#else + if (DEFAULT_ABI == ABI_V4 && flag_pic) + { + gcc_assert (!TARGET_SECURE_PLT); + return "bl %z1@plt"; + } + else + return "bl %z1"; +#endif +} + "DEFAULT_ABI == ABI_V4 + && TARGET_SECURE_PLT && flag_pic && !SYMBOL_REF_LOCAL_P (operands[1]) + && (INTVAL (operands[3]) & CALL_LONG) == 0" + [(parallel [(set (match_dup 0) + (call (mem:SI (match_dup 1)) + (match_dup 2))) + (use (match_dup 3)) + (use (match_dup 4)) + (clobber (reg:SI LR_REGNO))])] +{ + operands[4] = pic_offset_table_rtx; +} + [(set_attr "type" "branch,branch") + (set_attr "length" "4,8")]) + +(define_insn "*call_value_nonlocal_sysv_secure<mode>" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s,s")) + (match_operand 2 "" "g,g"))) + (use (match_operand:SI 3 "immediate_operand" "O,n")) + (use (match_operand:SI 4 "register_operand" "r,r")) + (clobber (reg:SI LR_REGNO))] + "(DEFAULT_ABI == ABI_V4 + && TARGET_SECURE_PLT && flag_pic && !SYMBOL_REF_LOCAL_P (operands[1]) + && (INTVAL (operands[3]) & CALL_LONG) == 0)" +{ + if (INTVAL (operands[3]) & CALL_V4_SET_FP_ARGS) + output_asm_insn ("crxor 6,6,6", operands); + + else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn ("creqv 6,6,6", operands); + + if (flag_pic == 2) + return "bl %z1+32768@plt"; + else + return "bl %z1@plt"; +} + [(set_attr "type" "branch,branch") + (set_attr "length" "4,8")]) + + +;; Call to AIX abi function in the same module. + +(define_insn "*call_local_aix<mode>" + [(call (mem:SI (match_operand:P 0 "current_file_function_operand" "s")) + (match_operand 1 "" "g")) + (clobber (reg:P LR_REGNO))] + "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2" + "bl %z0" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*call_value_local_aix<mode>" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:P 1 "current_file_function_operand" "s")) + (match_operand 2 "" "g"))) + (clobber (reg:P LR_REGNO))] + "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2" + "bl %z1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +;; Call to AIX abi function which may be in another module. +;; Restore the TOC pointer (r2) after the call. + +(define_insn "*call_nonlocal_aix<mode>" + [(call (mem:SI (match_operand:P 0 "symbol_ref_operand" "s")) + (match_operand 1 "" "g")) + (clobber (reg:P LR_REGNO))] + "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2" + "bl %z0\;nop" + [(set_attr "type" "branch") + (set_attr "length" "8")]) + +(define_insn "*call_value_nonlocal_aix<mode>" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:P 1 "symbol_ref_operand" "s")) + (match_operand 2 "" "g"))) + (clobber (reg:P LR_REGNO))] + "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2" + "bl %z1\;nop" + [(set_attr "type" "branch") + (set_attr "length" "8")]) + +;; Call to indirect functions with the AIX abi using a 3 word descriptor. +;; Operand0 is the addresss of the function to call +;; Operand2 is the location in the function descriptor to load r2 from +;; Operand3 is the offset of the stack location holding the current TOC pointer + +(define_insn "*call_indirect_aix<mode>" + [(call (mem:SI (match_operand:P 0 "register_operand" "c,*l")) + (match_operand 1 "" "g,g")) + (use (match_operand:P 2 "memory_operand" "<ptrm>,<ptrm>")) + (set (reg:P TOC_REGNUM) (unspec [(match_operand:P 3 "const_int_operand" "n,n")] UNSPEC_TOCSLOT)) + (clobber (reg:P LR_REGNO))] + "DEFAULT_ABI == ABI_AIX" + "<ptrload> 2,%2\;b%T0l\;<ptrload> 2,%3(1)" + [(set_attr "type" "jmpreg") + (set_attr "length" "12")]) + +(define_insn "*call_value_indirect_aix<mode>" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:P 1 "register_operand" "c,*l")) + (match_operand 2 "" "g,g"))) + (use (match_operand:P 3 "memory_operand" "<ptrm>,<ptrm>")) + (set (reg:P TOC_REGNUM) (unspec [(match_operand:P 4 "const_int_operand" "n,n")] UNSPEC_TOCSLOT)) + (clobber (reg:P LR_REGNO))] + "DEFAULT_ABI == ABI_AIX" + "<ptrload> 2,%3\;b%T1l\;<ptrload> 2,%4(1)" + [(set_attr "type" "jmpreg") + (set_attr "length" "12")]) + +;; Call to indirect functions with the ELFv2 ABI. +;; Operand0 is the addresss of the function to call +;; Operand2 is the offset of the stack location holding the current TOC pointer + +(define_insn "*call_indirect_elfv2<mode>" + [(call (mem:SI (match_operand:P 0 "register_operand" "c,*l")) + (match_operand 1 "" "g,g")) + (set (reg:P TOC_REGNUM) (unspec [(match_operand:P 2 "const_int_operand" "n,n")] UNSPEC_TOCSLOT)) + (clobber (reg:P LR_REGNO))] + "DEFAULT_ABI == ABI_ELFv2" + "b%T0l\;<ptrload> 2,%2(1)" + [(set_attr "type" "jmpreg") + (set_attr "length" "8")]) + +(define_insn "*call_value_indirect_elfv2<mode>" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:P 1 "register_operand" "c,*l")) + (match_operand 2 "" "g,g"))) + (set (reg:P TOC_REGNUM) (unspec [(match_operand:P 3 "const_int_operand" "n,n")] UNSPEC_TOCSLOT)) + (clobber (reg:P LR_REGNO))] + "DEFAULT_ABI == ABI_ELFv2" + "b%T1l\;<ptrload> 2,%3(1)" + [(set_attr "type" "jmpreg") + (set_attr "length" "8")]) + + +;; Call subroutine returning any type. +(define_expand "untyped_call" + [(parallel [(call (match_operand 0 "" "") + (const_int 0)) + (match_operand 1 "" "") + (match_operand 2 "" "")])] + "" + " +{ + int i; + + emit_call_insn (gen_call (operands[0], const0_rtx, const0_rtx)); + + for (i = 0; i < XVECLEN (operands[2], 0); i++) + { + rtx set = XVECEXP (operands[2], 0, i); + emit_move_insn (SET_DEST (set), SET_SRC (set)); + } + + /* The optimizer does not know that the call sets the function value + registers we stored in the result block. We avoid problems by + claiming that all hard registers are used and clobbered at this + point. */ + emit_insn (gen_blockage ()); + + DONE; +}") + +;; sibling call patterns +(define_expand "sibcall" + [(parallel [(call (mem:SI (match_operand 0 "address_operand" "")) + (match_operand 1 "" "")) + (use (match_operand 2 "" "")) + (simple_return)])] + "" + " +{ +#if TARGET_MACHO + if (MACHOPIC_INDIRECT) + operands[0] = machopic_indirect_call_target (operands[0]); +#endif + + gcc_assert (GET_CODE (operands[0]) == MEM); + gcc_assert (GET_CODE (operands[1]) == CONST_INT); + + operands[0] = XEXP (operands[0], 0); + + if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2) + { + rs6000_sibcall_aix (NULL_RTX, operands[0], operands[1], operands[2]); + DONE; + } +}") + +(define_expand "sibcall_value" + [(parallel [(set (match_operand 0 "register_operand" "") + (call (mem:SI (match_operand 1 "address_operand" "")) + (match_operand 2 "" ""))) + (use (match_operand 3 "" "")) + (simple_return)])] + "" + " +{ +#if TARGET_MACHO + if (MACHOPIC_INDIRECT) + operands[1] = machopic_indirect_call_target (operands[1]); +#endif + + gcc_assert (GET_CODE (operands[1]) == MEM); + gcc_assert (GET_CODE (operands[2]) == CONST_INT); + + operands[1] = XEXP (operands[1], 0); + + if (DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2) + { + rs6000_sibcall_aix (operands[0], operands[1], operands[2], operands[3]); + DONE; + } +}") + +(define_insn "*sibcall_local32" + [(call (mem:SI (match_operand:SI 0 "current_file_function_operand" "s,s")) + (match_operand 1 "" "g,g")) + (use (match_operand:SI 2 "immediate_operand" "O,n")) + (simple_return)] + "(INTVAL (operands[2]) & CALL_LONG) == 0" + "* +{ + if (INTVAL (operands[2]) & CALL_V4_SET_FP_ARGS) + output_asm_insn (\"crxor 6,6,6\", operands); + + else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn (\"creqv 6,6,6\", operands); + + return (DEFAULT_ABI == ABI_V4 && flag_pic) ? \"b %z0@local\" : \"b %z0\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "4,8")]) + +(define_insn "*sibcall_local64" + [(call (mem:SI (match_operand:DI 0 "current_file_function_operand" "s,s")) + (match_operand 1 "" "g,g")) + (use (match_operand:SI 2 "immediate_operand" "O,n")) + (simple_return)] + "TARGET_64BIT && (INTVAL (operands[2]) & CALL_LONG) == 0" + "* +{ + if (INTVAL (operands[2]) & CALL_V4_SET_FP_ARGS) + output_asm_insn (\"crxor 6,6,6\", operands); + + else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn (\"creqv 6,6,6\", operands); + + return (DEFAULT_ABI == ABI_V4 && flag_pic) ? \"b %z0@local\" : \"b %z0\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "4,8")]) + +(define_insn "*sibcall_value_local32" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:SI 1 "current_file_function_operand" "s,s")) + (match_operand 2 "" "g,g"))) + (use (match_operand:SI 3 "immediate_operand" "O,n")) + (simple_return)] + "(INTVAL (operands[3]) & CALL_LONG) == 0" + "* +{ + if (INTVAL (operands[3]) & CALL_V4_SET_FP_ARGS) + output_asm_insn (\"crxor 6,6,6\", operands); + + else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn (\"creqv 6,6,6\", operands); + + return (DEFAULT_ABI == ABI_V4 && flag_pic) ? \"b %z1@local\" : \"b %z1\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "4,8")]) + +(define_insn "*sibcall_value_local64" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:DI 1 "current_file_function_operand" "s,s")) + (match_operand 2 "" "g,g"))) + (use (match_operand:SI 3 "immediate_operand" "O,n")) + (simple_return)] + "TARGET_64BIT && (INTVAL (operands[3]) & CALL_LONG) == 0" + "* +{ + if (INTVAL (operands[3]) & CALL_V4_SET_FP_ARGS) + output_asm_insn (\"crxor 6,6,6\", operands); + + else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn (\"creqv 6,6,6\", operands); + + return (DEFAULT_ABI == ABI_V4 && flag_pic) ? \"b %z1@local\" : \"b %z1\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "4,8")]) + +(define_insn "*sibcall_nonlocal_sysv<mode>" + [(call (mem:SI (match_operand:P 0 "call_operand" "s,s,c,c")) + (match_operand 1 "" "")) + (use (match_operand 2 "immediate_operand" "O,n,O,n")) + (simple_return)] + "(DEFAULT_ABI == ABI_DARWIN + || DEFAULT_ABI == ABI_V4) + && (INTVAL (operands[2]) & CALL_LONG) == 0" + "* +{ + if (INTVAL (operands[2]) & CALL_V4_SET_FP_ARGS) + output_asm_insn (\"crxor 6,6,6\", operands); + + else if (INTVAL (operands[2]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn (\"creqv 6,6,6\", operands); + + if (which_alternative >= 2) + return \"b%T0\"; + else if (DEFAULT_ABI == ABI_V4 && flag_pic) + { + gcc_assert (!TARGET_SECURE_PLT); + return \"b %z0@plt\"; + } + else + return \"b %z0\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "4,8,4,8")]) + +(define_insn "*sibcall_value_nonlocal_sysv<mode>" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:P 1 "call_operand" "s,s,c,c")) + (match_operand 2 "" ""))) + (use (match_operand:SI 3 "immediate_operand" "O,n,O,n")) + (simple_return)] + "(DEFAULT_ABI == ABI_DARWIN + || DEFAULT_ABI == ABI_V4) + && (INTVAL (operands[3]) & CALL_LONG) == 0" + "* +{ + if (INTVAL (operands[3]) & CALL_V4_SET_FP_ARGS) + output_asm_insn (\"crxor 6,6,6\", operands); + + else if (INTVAL (operands[3]) & CALL_V4_CLEAR_FP_ARGS) + output_asm_insn (\"creqv 6,6,6\", operands); + + if (which_alternative >= 2) + return \"b%T1\"; + else if (DEFAULT_ABI == ABI_V4 && flag_pic) + { + gcc_assert (!TARGET_SECURE_PLT); + return \"b %z1@plt\"; + } + else + return \"b %z1\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "4,8,4,8")]) + +;; AIX ABI sibling call patterns. + +(define_insn "*sibcall_aix<mode>" + [(call (mem:SI (match_operand:P 0 "call_operand" "s,c")) + (match_operand 1 "" "g,g")) + (simple_return)] + "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2" + "@ + b %z0 + b%T0" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*sibcall_value_aix<mode>" + [(set (match_operand 0 "" "") + (call (mem:SI (match_operand:P 1 "call_operand" "s,c")) + (match_operand 2 "" "g,g"))) + (simple_return)] + "DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2" + "@ + b %z1 + b%T1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_expand "sibcall_epilogue" + [(use (const_int 0))] + "" +{ + if (!TARGET_SCHED_PROLOG) + emit_insn (gen_blockage ()); + rs6000_emit_epilogue (TRUE); + DONE; +}) + +;; UNSPEC_VOLATILE is considered to use and clobber all hard registers and +;; all of memory. This blocks insns from being moved across this point. + +(define_insn "blockage" + [(unspec_volatile [(const_int 0)] UNSPECV_BLOCK)] + "" + "") + +(define_expand "probe_stack_address" + [(use (match_operand 0 "address_operand"))] + "" +{ + operands[0] = gen_rtx_MEM (Pmode, operands[0]); + MEM_VOLATILE_P (operands[0]) = 1; + + if (TARGET_64BIT) + emit_insn (gen_probe_stack_di (operands[0])); + else + emit_insn (gen_probe_stack_si (operands[0])); + DONE; +}) + +(define_insn "probe_stack_<mode>" + [(set (match_operand:P 0 "memory_operand" "=m") + (unspec:P [(const_int 0)] UNSPEC_PROBE_STACK))] + "" +{ + operands[1] = gen_rtx_REG (Pmode, 0); + return "st<wd>%U0%X0 %1,%0"; +} + [(set_attr "type" "store") + (set (attr "update") + (if_then_else (match_operand 0 "update_address_mem") + (const_string "yes") + (const_string "no"))) + (set (attr "indexed") + (if_then_else (match_operand 0 "indexed_address_mem") + (const_string "yes") + (const_string "no"))) + (set_attr "length" "4")]) + +(define_insn "probe_stack_range<P:mode>" + [(set (match_operand:P 0 "register_operand" "=r") + (unspec_volatile:P [(match_operand:P 1 "register_operand" "0") + (match_operand:P 2 "register_operand" "r")] + UNSPECV_PROBE_STACK_RANGE))] + "" + "* return output_probe_stack_range (operands[0], operands[2]);" + [(set_attr "type" "three")]) + +;; Compare insns are next. Note that the RS/6000 has two types of compares, +;; signed & unsigned, and one type of branch. +;; +;; Start with the DEFINE_EXPANDs to generate the rtl for compares, scc +;; insns, and branches. + +(define_expand "cbranch<mode>4" + [(use (match_operator 0 "rs6000_cbranch_operator" + [(match_operand:GPR 1 "gpc_reg_operand" "") + (match_operand:GPR 2 "reg_or_short_operand" "")])) + (use (match_operand 3 ""))] + "" + " +{ + /* Take care of the possibility that operands[2] might be negative but + this might be a logical operation. That insn doesn't exist. */ + if (GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) < 0) + { + operands[2] = force_reg (<MODE>mode, operands[2]); + operands[0] = gen_rtx_fmt_ee (GET_CODE (operands[0]), + GET_MODE (operands[0]), + operands[1], operands[2]); + } + + rs6000_emit_cbranch (<MODE>mode, operands); + DONE; +}") + +(define_expand "cbranch<mode>4" + [(use (match_operator 0 "rs6000_cbranch_operator" + [(match_operand:FP 1 "gpc_reg_operand" "") + (match_operand:FP 2 "gpc_reg_operand" "")])) + (use (match_operand 3 ""))] + "" + " +{ + rs6000_emit_cbranch (<MODE>mode, operands); + DONE; +}") + +(define_expand "cstore<mode>4_signed" + [(use (match_operator 1 "signed_comparison_operator" + [(match_operand:P 2 "gpc_reg_operand") + (match_operand:P 3 "gpc_reg_operand")])) + (clobber (match_operand:P 0 "gpc_reg_operand"))] + "" +{ + enum rtx_code cond_code = GET_CODE (operands[1]); + + rtx op0 = operands[0]; + rtx op1 = operands[2]; + rtx op2 = operands[3]; + + if (cond_code == GE || cond_code == LT) + { + cond_code = swap_condition (cond_code); + std::swap (op1, op2); + } + + rtx tmp1 = gen_reg_rtx (<MODE>mode); + rtx tmp2 = gen_reg_rtx (<MODE>mode); + rtx tmp3 = gen_reg_rtx (<MODE>mode); + + int sh = GET_MODE_BITSIZE (<MODE>mode) - 1; + emit_insn (gen_lshr<mode>3 (tmp1, op1, GEN_INT (sh))); + emit_insn (gen_ashr<mode>3 (tmp2, op2, GEN_INT (sh))); + + emit_insn (gen_subf<mode>3_carry (tmp3, op1, op2)); + + if (cond_code == LE) + emit_insn (gen_add<mode>3_carry_in (op0, tmp1, tmp2)); + else + { + rtx tmp4 = gen_reg_rtx (<MODE>mode); + emit_insn (gen_add<mode>3_carry_in (tmp4, tmp1, tmp2)); + emit_insn (gen_xor<mode>3 (op0, tmp4, const1_rtx)); + } + + DONE; +}) + +(define_expand "cstore<mode>4_unsigned" + [(use (match_operator 1 "unsigned_comparison_operator" + [(match_operand:P 2 "gpc_reg_operand") + (match_operand:P 3 "reg_or_short_operand")])) + (clobber (match_operand:P 0 "gpc_reg_operand"))] + "" +{ + enum rtx_code cond_code = GET_CODE (operands[1]); + + rtx op0 = operands[0]; + rtx op1 = operands[2]; + rtx op2 = operands[3]; + + if (cond_code == GEU || cond_code == LTU) + { + cond_code = swap_condition (cond_code); + std::swap (op1, op2); + } + + if (!gpc_reg_operand (op1, <MODE>mode)) + op1 = force_reg (<MODE>mode, op1); + if (!reg_or_short_operand (op2, <MODE>mode)) + op2 = force_reg (<MODE>mode, op2); + + rtx tmp = gen_reg_rtx (<MODE>mode); + rtx tmp2 = gen_reg_rtx (<MODE>mode); + + emit_insn (gen_subf<mode>3_carry (tmp, op1, op2)); + emit_insn (gen_subf<mode>3_carry_in_xx (tmp2)); + + if (cond_code == LEU) + emit_insn (gen_add<mode>3 (op0, tmp2, const1_rtx)); + else + emit_insn (gen_neg<mode>2 (op0, tmp2)); + + DONE; +}) + +(define_expand "cstore_si_as_di" + [(use (match_operator 1 "unsigned_comparison_operator" + [(match_operand:SI 2 "gpc_reg_operand") + (match_operand:SI 3 "reg_or_short_operand")])) + (clobber (match_operand:SI 0 "gpc_reg_operand"))] + "" +{ + int uns_flag = unsigned_comparison_operator (operands[1], VOIDmode) ? 1 : 0; + enum rtx_code cond_code = signed_condition (GET_CODE (operands[1])); + + operands[2] = force_reg (SImode, operands[2]); + operands[3] = force_reg (SImode, operands[3]); + rtx op1 = gen_reg_rtx (DImode); + rtx op2 = gen_reg_rtx (DImode); + convert_move (op1, operands[2], uns_flag); + convert_move (op2, operands[3], uns_flag); + + if (cond_code == GT || cond_code == LE) + { + cond_code = swap_condition (cond_code); + std::swap (op1, op2); + } + + rtx tmp = gen_reg_rtx (DImode); + rtx tmp2 = gen_reg_rtx (DImode); + emit_insn (gen_subdi3 (tmp, op1, op2)); + emit_insn (gen_lshrdi3 (tmp2, tmp, GEN_INT (63))); + + rtx tmp3; + switch (cond_code) + { + default: + gcc_unreachable (); + case LT: + tmp3 = tmp2; + break; + case GE: + tmp3 = gen_reg_rtx (DImode); + emit_insn (gen_xordi3 (tmp3, tmp2, const1_rtx)); + break; + } + + convert_move (operands[0], tmp3, 1); + + DONE; +}) + +(define_expand "cstore<mode>4_signed_imm" + [(use (match_operator 1 "signed_comparison_operator" + [(match_operand:GPR 2 "gpc_reg_operand") + (match_operand:GPR 3 "immediate_operand")])) + (clobber (match_operand:GPR 0 "gpc_reg_operand"))] + "" +{ + bool invert = false; + + enum rtx_code cond_code = GET_CODE (operands[1]); + + rtx op0 = operands[0]; + rtx op1 = operands[2]; + HOST_WIDE_INT val = INTVAL (operands[3]); + + if (cond_code == GE || cond_code == GT) + { + cond_code = reverse_condition (cond_code); + invert = true; + } + + if (cond_code == LE) + val++; + + rtx tmp = gen_reg_rtx (<MODE>mode); + emit_insn (gen_add<mode>3 (tmp, op1, GEN_INT (-val))); + rtx x = gen_reg_rtx (<MODE>mode); + if (val < 0) + emit_insn (gen_and<mode>3 (x, op1, tmp)); + else + emit_insn (gen_ior<mode>3 (x, op1, tmp)); + + if (invert) + { + rtx tmp = gen_reg_rtx (<MODE>mode); + emit_insn (gen_one_cmpl<mode>2 (tmp, x)); + x = tmp; + } + + int sh = GET_MODE_BITSIZE (<MODE>mode) - 1; + emit_insn (gen_lshr<mode>3 (op0, x, GEN_INT (sh))); + + DONE; +}) + +(define_expand "cstore<mode>4_unsigned_imm" + [(use (match_operator 1 "unsigned_comparison_operator" + [(match_operand:GPR 2 "gpc_reg_operand") + (match_operand:GPR 3 "immediate_operand")])) + (clobber (match_operand:GPR 0 "gpc_reg_operand"))] + "" +{ + bool invert = false; + + enum rtx_code cond_code = GET_CODE (operands[1]); + + rtx op0 = operands[0]; + rtx op1 = operands[2]; + HOST_WIDE_INT val = INTVAL (operands[3]); + + if (cond_code == GEU || cond_code == GTU) + { + cond_code = reverse_condition (cond_code); + invert = true; + } + + if (cond_code == LEU) + val++; + + rtx tmp = gen_reg_rtx (<MODE>mode); + rtx tmp2 = gen_reg_rtx (<MODE>mode); + emit_insn (gen_add<mode>3 (tmp, op1, GEN_INT (-val))); + emit_insn (gen_one_cmpl<mode>2 (tmp2, op1)); + rtx x = gen_reg_rtx (<MODE>mode); + if (val < 0) + emit_insn (gen_ior<mode>3 (x, tmp, tmp2)); + else + emit_insn (gen_and<mode>3 (x, tmp, tmp2)); + + if (invert) + { + rtx tmp = gen_reg_rtx (<MODE>mode); + emit_insn (gen_one_cmpl<mode>2 (tmp, x)); + x = tmp; + } + + int sh = GET_MODE_BITSIZE (<MODE>mode) - 1; + emit_insn (gen_lshr<mode>3 (op0, x, GEN_INT (sh))); + + DONE; +}) + +(define_expand "cstore<mode>4" + [(use (match_operator 1 "rs6000_cbranch_operator" + [(match_operand:GPR 2 "gpc_reg_operand") + (match_operand:GPR 3 "reg_or_short_operand")])) + (clobber (match_operand:GPR 0 "gpc_reg_operand"))] + "" +{ + /* Use ISEL if the user asked for it. */ + if (TARGET_ISEL) + rs6000_emit_sISEL (<MODE>mode, operands); + + /* Expanding EQ and NE directly to some machine instructions does not help + but does hurt combine. So don't. */ + else if (GET_CODE (operands[1]) == EQ) + emit_insn (gen_eq<mode>3 (operands[0], operands[2], operands[3])); + else if (<MODE>mode == Pmode + && GET_CODE (operands[1]) == NE) + emit_insn (gen_ne<mode>3 (operands[0], operands[2], operands[3])); + else if (GET_CODE (operands[1]) == NE) + { + rtx tmp = gen_reg_rtx (<MODE>mode); + emit_insn (gen_eq<mode>3 (tmp, operands[2], operands[3])); + emit_insn (gen_xor<mode>3 (operands[0], tmp, const1_rtx)); + } + + /* Expanding the unsigned comparisons however helps a lot: all the neg_ltu + etc. combinations magically work out just right. */ + else if (<MODE>mode == Pmode + && unsigned_comparison_operator (operands[1], VOIDmode)) + emit_insn (gen_cstore<mode>4_unsigned (operands[0], operands[1], + operands[2], operands[3])); + + /* For comparisons smaller than Pmode we can cheaply do things in Pmode. */ + else if (<MODE>mode == SImode && Pmode == DImode) + emit_insn (gen_cstore_si_as_di (operands[0], operands[1], + operands[2], operands[3])); + + /* For signed comparisons against a constant, we can do some simple + bit-twiddling. */ + else if (signed_comparison_operator (operands[1], VOIDmode) + && CONST_INT_P (operands[3])) + emit_insn (gen_cstore<mode>4_signed_imm (operands[0], operands[1], + operands[2], operands[3])); + + /* And similarly for unsigned comparisons. */ + else if (unsigned_comparison_operator (operands[1], VOIDmode) + && CONST_INT_P (operands[3])) + emit_insn (gen_cstore<mode>4_unsigned_imm (operands[0], operands[1], + operands[2], operands[3])); + + /* We also do not want to use mfcr for signed comparisons. */ + else if (<MODE>mode == Pmode + && signed_comparison_operator (operands[1], VOIDmode)) + emit_insn (gen_cstore<mode>4_signed (operands[0], operands[1], + operands[2], operands[3])); + + /* Everything else, use the mfcr brute force. */ + else + rs6000_emit_sCOND (<MODE>mode, operands); + + DONE; +}) + +(define_expand "cstore<mode>4" + [(use (match_operator 1 "rs6000_cbranch_operator" + [(match_operand:FP 2 "gpc_reg_operand") + (match_operand:FP 3 "gpc_reg_operand")])) + (clobber (match_operand:SI 0 "gpc_reg_operand"))] + "" +{ + rs6000_emit_sCOND (<MODE>mode, operands); + DONE; +}) + + +(define_expand "stack_protect_set" + [(match_operand 0 "memory_operand") + (match_operand 1 "memory_operand")] + "" +{ + if (rs6000_stack_protector_guard == SSP_TLS) + { + rtx reg = gen_rtx_REG (Pmode, rs6000_stack_protector_guard_reg); + rtx offset = GEN_INT (rs6000_stack_protector_guard_offset); + rtx addr = gen_rtx_PLUS (Pmode, reg, offset); + operands[1] = gen_rtx_MEM (Pmode, addr); + } + + if (TARGET_64BIT) + emit_insn (gen_stack_protect_setdi (operands[0], operands[1])); + else + emit_insn (gen_stack_protect_setsi (operands[0], operands[1])); + + DONE; +}) + +(define_insn "stack_protect_setsi" + [(set (match_operand:SI 0 "memory_operand" "=m") + (unspec:SI [(match_operand:SI 1 "memory_operand" "m")] UNSPEC_SP_SET)) + (set (match_scratch:SI 2 "=&r") (const_int 0))] + "TARGET_32BIT" + "lwz%U1%X1 %2,%1\;stw%U0%X0 %2,%0\;li %2,0" + [(set_attr "type" "three") + (set_attr "length" "12")]) + +(define_insn "stack_protect_setdi" + [(set (match_operand:DI 0 "memory_operand" "=Y") + (unspec:DI [(match_operand:DI 1 "memory_operand" "Y")] UNSPEC_SP_SET)) + (set (match_scratch:DI 2 "=&r") (const_int 0))] + "TARGET_64BIT" + "ld%U1%X1 %2,%1\;std%U0%X0 %2,%0\;li %2,0" + [(set_attr "type" "three") + (set_attr "length" "12")]) + +(define_expand "stack_protect_test" + [(match_operand 0 "memory_operand") + (match_operand 1 "memory_operand") + (match_operand 2 "")] + "" +{ + rtx guard = operands[1]; + + if (rs6000_stack_protector_guard == SSP_TLS) + { + rtx reg = gen_rtx_REG (Pmode, rs6000_stack_protector_guard_reg); + rtx offset = GEN_INT (rs6000_stack_protector_guard_offset); + rtx addr = gen_rtx_PLUS (Pmode, reg, offset); + guard = gen_rtx_MEM (Pmode, addr); + } + + operands[1] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, guard), UNSPEC_SP_TEST); + rtx test = gen_rtx_EQ (VOIDmode, operands[0], operands[1]); + rtx jump = gen_cbranchsi4 (test, operands[0], operands[1], operands[2]); + emit_jump_insn (jump); + + DONE; +}) + +(define_insn "stack_protect_testsi" + [(set (match_operand:CCEQ 0 "cc_reg_operand" "=x,?y") + (unspec:CCEQ [(match_operand:SI 1 "memory_operand" "m,m") + (match_operand:SI 2 "memory_operand" "m,m")] + UNSPEC_SP_TEST)) + (set (match_scratch:SI 4 "=r,r") (const_int 0)) + (clobber (match_scratch:SI 3 "=&r,&r"))] + "TARGET_32BIT" + "@ + lwz%U1%X1 %3,%1\;lwz%U2%X2 %4,%2\;xor. %3,%3,%4\;li %4,0 + lwz%U1%X1 %3,%1\;lwz%U2%X2 %4,%2\;cmplw %0,%3,%4\;li %3,0\;li %4,0" + [(set_attr "length" "16,20")]) + +(define_insn "stack_protect_testdi" + [(set (match_operand:CCEQ 0 "cc_reg_operand" "=x,?y") + (unspec:CCEQ [(match_operand:DI 1 "memory_operand" "Y,Y") + (match_operand:DI 2 "memory_operand" "Y,Y")] + UNSPEC_SP_TEST)) + (set (match_scratch:DI 4 "=r,r") (const_int 0)) + (clobber (match_scratch:DI 3 "=&r,&r"))] + "TARGET_64BIT" + "@ + ld%U1%X1 %3,%1\;ld%U2%X2 %4,%2\;xor. %3,%3,%4\;li %4,0 + ld%U1%X1 %3,%1\;ld%U2%X2 %4,%2\;cmpld %0,%3,%4\;li %3,0\;li %4,0" + [(set_attr "length" "16,20")]) + + +;; Here are the actual compare insns. +(define_insn "*cmp<mode>_signed" + [(set (match_operand:CC 0 "cc_reg_operand" "=y") + (compare:CC (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "reg_or_short_operand" "rI")))] + "" + "cmp<wd>%I2 %0,%1,%2" + [(set_attr "type" "cmp")]) + +(define_insn "*cmp<mode>_unsigned" + [(set (match_operand:CCUNS 0 "cc_reg_operand" "=y") + (compare:CCUNS (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "reg_or_u_short_operand" "rK")))] + "" + "cmpl<wd>%I2 %0,%1,%2" + [(set_attr "type" "cmp")]) + +;; If we are comparing a register for equality with a large constant, +;; we can do this with an XOR followed by a compare. But this is profitable +;; only if the large constant is only used for the comparison (and in this +;; case we already have a register to reuse as scratch). +;; +;; For 64-bit registers, we could only do so if the constant's bit 15 is clear: +;; otherwise we'd need to XOR with FFFFFFFF????0000 which is not available. + +(define_peephole2 + [(set (match_operand:SI 0 "register_operand") + (match_operand:SI 1 "logical_const_operand" "")) + (set (match_dup 0) (match_operator:SI 3 "boolean_or_operator" + [(match_dup 0) + (match_operand:SI 2 "logical_const_operand" "")])) + (set (match_operand:CC 4 "cc_reg_operand" "") + (compare:CC (match_operand:SI 5 "gpc_reg_operand" "") + (match_dup 0))) + (set (pc) + (if_then_else (match_operator 6 "equality_operator" + [(match_dup 4) (const_int 0)]) + (match_operand 7 "" "") + (match_operand 8 "" "")))] + "peep2_reg_dead_p (3, operands[0]) + && peep2_reg_dead_p (4, operands[4]) + && REGNO (operands[0]) != REGNO (operands[5])" + [(set (match_dup 0) (xor:SI (match_dup 5) (match_dup 9))) + (set (match_dup 4) (compare:CC (match_dup 0) (match_dup 10))) + (set (pc) (if_then_else (match_dup 6) (match_dup 7) (match_dup 8)))] + +{ + /* Get the constant we are comparing against, and see what it looks like + when sign-extended from 16 to 32 bits. Then see what constant we could + XOR with SEXTC to get the sign-extended value. */ + rtx cnst = simplify_const_binary_operation (GET_CODE (operands[3]), + SImode, + operands[1], operands[2]); + HOST_WIDE_INT c = INTVAL (cnst); + HOST_WIDE_INT sextc = ((c & 0xffff) ^ 0x8000) - 0x8000; + HOST_WIDE_INT xorv = c ^ sextc; + + operands[9] = GEN_INT (xorv); + operands[10] = GEN_INT (sextc); +}) + +;; The following two insns don't exist as single insns, but if we provide +;; them, we can swap an add and compare, which will enable us to overlap more +;; of the required delay between a compare and branch. We generate code for +;; them by splitting. + +(define_insn "" + [(set (match_operand:CC 3 "cc_reg_operand" "=y") + (compare:CC (match_operand:SI 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "short_cint_operand" "i"))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (match_dup 1) (match_operand:SI 4 "short_cint_operand" "i")))] + "" + "#" + [(set_attr "length" "8")]) + +(define_insn "" + [(set (match_operand:CCUNS 3 "cc_reg_operand" "=y") + (compare:CCUNS (match_operand:SI 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "u_short_cint_operand" "i"))) + (set (match_operand:SI 0 "gpc_reg_operand" "=r") + (plus:SI (match_dup 1) (match_operand:SI 4 "short_cint_operand" "i")))] + "" + "#" + [(set_attr "length" "8")]) + +(define_split + [(set (match_operand:CC 3 "cc_reg_operand" "") + (compare:CC (match_operand:SI 1 "gpc_reg_operand" "") + (match_operand:SI 2 "short_cint_operand" ""))) + (set (match_operand:SI 0 "gpc_reg_operand" "") + (plus:SI (match_dup 1) (match_operand:SI 4 "short_cint_operand" "")))] + "" + [(set (match_dup 3) (compare:CC (match_dup 1) (match_dup 2))) + (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 4)))]) + +(define_split + [(set (match_operand:CCUNS 3 "cc_reg_operand" "") + (compare:CCUNS (match_operand:SI 1 "gpc_reg_operand" "") + (match_operand:SI 2 "u_short_cint_operand" ""))) + (set (match_operand:SI 0 "gpc_reg_operand" "") + (plus:SI (match_dup 1) (match_operand:SI 4 "short_cint_operand" "")))] + "" + [(set (match_dup 3) (compare:CCUNS (match_dup 1) (match_dup 2))) + (set (match_dup 0) (plus:SI (match_dup 1) (match_dup 4)))]) + +;; Only need to compare second words if first words equal +(define_insn "*cmp<mode>_internal1" + [(set (match_operand:CCFP 0 "cc_reg_operand" "=y") + (compare:CCFP (match_operand:IBM128 1 "gpc_reg_operand" "d") + (match_operand:IBM128 2 "gpc_reg_operand" "d")))] + "!TARGET_XL_COMPAT && FLOAT128_IBM_P (<MODE>mode) + && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LONG_DOUBLE_128" + "fcmpu %0,%1,%2\;bne %0,$+8\;fcmpu %0,%L1,%L2" + [(set_attr "type" "fpcompare") + (set_attr "length" "12")]) + +(define_insn_and_split "*cmp<mode>_internal2" + [(set (match_operand:CCFP 0 "cc_reg_operand" "=y") + (compare:CCFP (match_operand:IBM128 1 "gpc_reg_operand" "d") + (match_operand:IBM128 2 "gpc_reg_operand" "d"))) + (clobber (match_scratch:DF 3 "=d")) + (clobber (match_scratch:DF 4 "=d")) + (clobber (match_scratch:DF 5 "=d")) + (clobber (match_scratch:DF 6 "=d")) + (clobber (match_scratch:DF 7 "=d")) + (clobber (match_scratch:DF 8 "=d")) + (clobber (match_scratch:DF 9 "=d")) + (clobber (match_scratch:DF 10 "=d")) + (clobber (match_scratch:GPR 11 "=b"))] + "TARGET_XL_COMPAT && FLOAT128_IBM_P (<MODE>mode) + && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT && TARGET_LONG_DOUBLE_128" + "#" + "&& reload_completed" + [(set (match_dup 3) (match_dup 14)) + (set (match_dup 4) (match_dup 15)) + (set (match_dup 9) (abs:DF (match_dup 5))) + (set (match_dup 0) (compare:CCFP (match_dup 9) (match_dup 3))) + (set (pc) (if_then_else (ne (match_dup 0) (const_int 0)) + (label_ref (match_dup 12)) + (pc))) + (set (match_dup 0) (compare:CCFP (match_dup 5) (match_dup 7))) + (set (pc) (label_ref (match_dup 13))) + (match_dup 12) + (set (match_dup 10) (minus:DF (match_dup 5) (match_dup 7))) + (set (match_dup 9) (minus:DF (match_dup 6) (match_dup 8))) + (set (match_dup 9) (plus:DF (match_dup 10) (match_dup 9))) + (set (match_dup 0) (compare:CCFP (match_dup 9) (match_dup 4))) + (match_dup 13)] +{ + REAL_VALUE_TYPE rv; + const int lo_word = LONG_DOUBLE_LARGE_FIRST ? GET_MODE_SIZE (DFmode) : 0; + const int hi_word = LONG_DOUBLE_LARGE_FIRST ? 0 : GET_MODE_SIZE (DFmode); + + operands[5] = simplify_gen_subreg (DFmode, operands[1], <MODE>mode, hi_word); + operands[6] = simplify_gen_subreg (DFmode, operands[1], <MODE>mode, lo_word); + operands[7] = simplify_gen_subreg (DFmode, operands[2], <MODE>mode, hi_word); + operands[8] = simplify_gen_subreg (DFmode, operands[2], <MODE>mode, lo_word); + operands[12] = gen_label_rtx (); + operands[13] = gen_label_rtx (); + real_inf (&rv); + operands[14] = force_const_mem (DFmode, + const_double_from_real_value (rv, DFmode)); + operands[15] = force_const_mem (DFmode, + const_double_from_real_value (dconst0, + DFmode)); + if (TARGET_TOC) + { + rtx tocref; + tocref = create_TOC_reference (XEXP (operands[14], 0), operands[11]); + operands[14] = gen_const_mem (DFmode, tocref); + tocref = create_TOC_reference (XEXP (operands[15], 0), operands[11]); + operands[15] = gen_const_mem (DFmode, tocref); + set_mem_alias_set (operands[14], get_TOC_alias_set ()); + set_mem_alias_set (operands[15], get_TOC_alias_set ()); + } +}) + +;; Now we have the scc insns. We can do some combinations because of the +;; way the machine works. +;; +;; Note that this is probably faster if we can put an insn between the +;; mfcr and rlinm, but this is tricky. Let's leave it for now. In most +;; cases the insns below which don't use an intermediate CR field will +;; be used instead. +(define_insn "" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (match_operator:SI 1 "scc_comparison_operator" + [(match_operand 2 "cc_reg_operand" "y") + (const_int 0)]))] + "" + "mfcr %0%Q2\;rlwinm %0,%0,%J1,1" + [(set (attr "type") + (cond [(match_test "TARGET_MFCRF") + (const_string "mfcrf") + ] + (const_string "mfcr"))) + (set_attr "length" "8")]) + +;; Same as above, but get the GT bit. +(define_insn "move_from_CR_gt_bit" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (unspec:SI [(match_operand 1 "cc_reg_operand" "y")] UNSPEC_MV_CR_GT))] + "TARGET_HARD_FLOAT && !TARGET_FPRS" + "mfcr %0\;rlwinm %0,%0,%D1,31,31" + [(set_attr "type" "mfcr") + (set_attr "length" "8")]) + +;; Same as above, but get the OV/ORDERED bit. +(define_insn "move_from_CR_ov_bit" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (unspec:SI [(match_operand:CC 1 "cc_reg_operand" "y")] + UNSPEC_MV_CR_OV))] + "TARGET_ISEL" + "mfcr %0\;rlwinm %0,%0,%t1,1" + [(set_attr "type" "mfcr") + (set_attr "length" "8")]) + +(define_insn "" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (match_operator:DI 1 "scc_comparison_operator" + [(match_operand 2 "cc_reg_operand" "y") + (const_int 0)]))] + "TARGET_POWERPC64" + "mfcr %0%Q2\;rlwinm %0,%0,%J1,1" + [(set (attr "type") + (cond [(match_test "TARGET_MFCRF") + (const_string "mfcrf") + ] + (const_string "mfcr"))) + (set_attr "length" "8")]) + +(define_insn "" + [(set (match_operand:CC 0 "cc_reg_operand" "=x,?y") + (compare:CC (match_operator:SI 1 "scc_comparison_operator" + [(match_operand 2 "cc_reg_operand" "y,y") + (const_int 0)]) + (const_int 0))) + (set (match_operand:SI 3 "gpc_reg_operand" "=r,r") + (match_op_dup 1 [(match_dup 2) (const_int 0)]))] + "TARGET_32BIT" + "@ + mfcr %3%Q2\;rlwinm. %3,%3,%J1,1 + #" + [(set_attr "type" "shift") + (set_attr "dot" "yes") + (set_attr "length" "8,16")]) + +(define_split + [(set (match_operand:CC 0 "cc_reg_not_cr0_operand" "") + (compare:CC (match_operator:SI 1 "scc_comparison_operator" + [(match_operand 2 "cc_reg_operand" "") + (const_int 0)]) + (const_int 0))) + (set (match_operand:SI 3 "gpc_reg_operand" "") + (match_op_dup 1 [(match_dup 2) (const_int 0)]))] + "TARGET_32BIT && reload_completed" + [(set (match_dup 3) + (match_op_dup 1 [(match_dup 2) (const_int 0)])) + (set (match_dup 0) + (compare:CC (match_dup 3) + (const_int 0)))] + "") + +(define_insn "" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (ashift:SI (match_operator:SI 1 "scc_comparison_operator" + [(match_operand 2 "cc_reg_operand" "y") + (const_int 0)]) + (match_operand:SI 3 "const_int_operand" "n")))] + "" + "* +{ + int is_bit = ccr_bit (operands[1], 1); + int put_bit = 31 - (INTVAL (operands[3]) & 31); + int count; + + if (is_bit >= put_bit) + count = is_bit - put_bit; + else + count = 32 - (put_bit - is_bit); + + operands[4] = GEN_INT (count); + operands[5] = GEN_INT (put_bit); + + return \"mfcr %0%Q2\;rlwinm %0,%0,%4,%5,%5\"; +}" + [(set (attr "type") + (cond [(match_test "TARGET_MFCRF") + (const_string "mfcrf") + ] + (const_string "mfcr"))) + (set_attr "length" "8")]) + +(define_insn "" + [(set (match_operand:CC 0 "cc_reg_operand" "=x,?y") + (compare:CC + (ashift:SI (match_operator:SI 1 "scc_comparison_operator" + [(match_operand 2 "cc_reg_operand" "y,y") + (const_int 0)]) + (match_operand:SI 3 "const_int_operand" "n,n")) + (const_int 0))) + (set (match_operand:SI 4 "gpc_reg_operand" "=r,r") + (ashift:SI (match_op_dup 1 [(match_dup 2) (const_int 0)]) + (match_dup 3)))] + "" + "* +{ + int is_bit = ccr_bit (operands[1], 1); + int put_bit = 31 - (INTVAL (operands[3]) & 31); + int count; + + /* Force split for non-cc0 compare. */ + if (which_alternative == 1) + return \"#\"; + + if (is_bit >= put_bit) + count = is_bit - put_bit; + else + count = 32 - (put_bit - is_bit); + + operands[5] = GEN_INT (count); + operands[6] = GEN_INT (put_bit); + + return \"mfcr %4%Q2\;rlwinm. %4,%4,%5,%6,%6\"; +}" + [(set_attr "type" "shift") + (set_attr "dot" "yes") + (set_attr "length" "8,16")]) + +(define_split + [(set (match_operand:CC 0 "cc_reg_not_micro_cr0_operand" "") + (compare:CC + (ashift:SI (match_operator:SI 1 "scc_comparison_operator" + [(match_operand 2 "cc_reg_operand" "") + (const_int 0)]) + (match_operand:SI 3 "const_int_operand" "")) + (const_int 0))) + (set (match_operand:SI 4 "gpc_reg_operand" "") + (ashift:SI (match_op_dup 1 [(match_dup 2) (const_int 0)]) + (match_dup 3)))] + "reload_completed" + [(set (match_dup 4) + (ashift:SI (match_op_dup 1 [(match_dup 2) (const_int 0)]) + (match_dup 3))) + (set (match_dup 0) + (compare:CC (match_dup 4) + (const_int 0)))] + "") + + +(define_mode_attr scc_eq_op2 [(SI "rKLI") + (DI "rKJI")]) + +(define_insn_and_split "eq<mode>3" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (eq:GPR (match_operand:GPR 1 "gpc_reg_operand" "r") + (match_operand:GPR 2 "scc_eq_operand" "<scc_eq_op2>"))) + (clobber (match_scratch:GPR 3 "=r")) + (clobber (match_scratch:GPR 4 "=r"))] + "" + "#" + "" + [(set (match_dup 4) + (clz:GPR (match_dup 3))) + (set (match_dup 0) + (lshiftrt:GPR (match_dup 4) + (match_dup 5)))] +{ + operands[3] = rs6000_emit_eqne (<MODE>mode, + operands[1], operands[2], operands[3]); + + if (GET_CODE (operands[4]) == SCRATCH) + operands[4] = gen_reg_rtx (<MODE>mode); + + operands[5] = GEN_INT (exact_log2 (GET_MODE_BITSIZE (<MODE>mode))); +} + [(set (attr "length") + (if_then_else (match_test "operands[2] == const0_rtx") + (const_string "8") + (const_string "12")))]) + +(define_insn_and_split "ne<mode>3" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (ne:P (match_operand:P 1 "gpc_reg_operand" "r") + (match_operand:P 2 "scc_eq_operand" "<scc_eq_op2>"))) + (clobber (match_scratch:P 3 "=r")) + (clobber (match_scratch:P 4 "=r")) + (clobber (reg:P CA_REGNO))] + "!TARGET_ISEL" + "#" + "" + [(parallel [(set (match_dup 4) + (plus:P (match_dup 3) + (const_int -1))) + (set (reg:P CA_REGNO) + (ne:P (match_dup 3) + (const_int 0)))]) + (parallel [(set (match_dup 0) + (plus:P (plus:P (not:P (match_dup 4)) + (reg:P CA_REGNO)) + (match_dup 3))) + (clobber (reg:P CA_REGNO))])] +{ + operands[3] = rs6000_emit_eqne (<MODE>mode, + operands[1], operands[2], operands[3]); + + if (GET_CODE (operands[4]) == SCRATCH) + operands[4] = gen_reg_rtx (<MODE>mode); +} + [(set (attr "length") + (if_then_else (match_test "operands[2] == const0_rtx") + (const_string "8") + (const_string "12")))]) + +(define_insn_and_split "*neg_eq_<mode>" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (neg:P (eq:P (match_operand:P 1 "gpc_reg_operand" "r") + (match_operand:P 2 "scc_eq_operand" "<scc_eq_op2>")))) + (clobber (match_scratch:P 3 "=r")) + (clobber (match_scratch:P 4 "=r")) + (clobber (reg:P CA_REGNO))] + "" + "#" + "" + [(parallel [(set (match_dup 4) + (plus:P (match_dup 3) + (const_int -1))) + (set (reg:P CA_REGNO) + (ne:P (match_dup 3) + (const_int 0)))]) + (parallel [(set (match_dup 0) + (plus:P (reg:P CA_REGNO) + (const_int -1))) + (clobber (reg:P CA_REGNO))])] +{ + operands[3] = rs6000_emit_eqne (<MODE>mode, + operands[1], operands[2], operands[3]); + + if (GET_CODE (operands[4]) == SCRATCH) + operands[4] = gen_reg_rtx (<MODE>mode); +} + [(set (attr "length") + (if_then_else (match_test "operands[2] == const0_rtx") + (const_string "8") + (const_string "12")))]) + +(define_insn_and_split "*neg_ne_<mode>" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (neg:P (ne:P (match_operand:P 1 "gpc_reg_operand" "r") + (match_operand:P 2 "scc_eq_operand" "<scc_eq_op2>")))) + (clobber (match_scratch:P 3 "=r")) + (clobber (match_scratch:P 4 "=r")) + (clobber (reg:P CA_REGNO))] + "" + "#" + "" + [(parallel [(set (match_dup 4) + (neg:P (match_dup 3))) + (set (reg:P CA_REGNO) + (eq:P (match_dup 3) + (const_int 0)))]) + (parallel [(set (match_dup 0) + (plus:P (reg:P CA_REGNO) + (const_int -1))) + (clobber (reg:P CA_REGNO))])] +{ + operands[3] = rs6000_emit_eqne (<MODE>mode, + operands[1], operands[2], operands[3]); + + if (GET_CODE (operands[4]) == SCRATCH) + operands[4] = gen_reg_rtx (<MODE>mode); +} + [(set (attr "length") + (if_then_else (match_test "operands[2] == const0_rtx") + (const_string "8") + (const_string "12")))]) + +(define_insn_and_split "*plus_eq_<mode>" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (plus:P (eq:P (match_operand:P 1 "gpc_reg_operand" "r") + (match_operand:P 2 "scc_eq_operand" "<scc_eq_op2>")) + (match_operand:P 3 "gpc_reg_operand" "r"))) + (clobber (match_scratch:P 4 "=r")) + (clobber (match_scratch:P 5 "=r")) + (clobber (reg:P CA_REGNO))] + "" + "#" + "" + [(parallel [(set (match_dup 5) + (neg:P (match_dup 4))) + (set (reg:P CA_REGNO) + (eq:P (match_dup 4) + (const_int 0)))]) + (parallel [(set (match_dup 0) + (plus:P (match_dup 3) + (reg:P CA_REGNO))) + (clobber (reg:P CA_REGNO))])] +{ + operands[4] = rs6000_emit_eqne (<MODE>mode, + operands[1], operands[2], operands[4]); + + if (GET_CODE (operands[5]) == SCRATCH) + operands[5] = gen_reg_rtx (<MODE>mode); +} + [(set (attr "length") + (if_then_else (match_test "operands[2] == const0_rtx") + (const_string "8") + (const_string "12")))]) + +(define_insn_and_split "*plus_ne_<mode>" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (plus:P (ne:P (match_operand:P 1 "gpc_reg_operand" "r") + (match_operand:P 2 "scc_eq_operand" "<scc_eq_op2>")) + (match_operand:P 3 "gpc_reg_operand" "r"))) + (clobber (match_scratch:P 4 "=r")) + (clobber (match_scratch:P 5 "=r")) + (clobber (reg:P CA_REGNO))] + "" + "#" + "" + [(parallel [(set (match_dup 5) + (plus:P (match_dup 4) + (const_int -1))) + (set (reg:P CA_REGNO) + (ne:P (match_dup 4) + (const_int 0)))]) + (parallel [(set (match_dup 0) + (plus:P (match_dup 3) + (reg:P CA_REGNO))) + (clobber (reg:P CA_REGNO))])] +{ + operands[4] = rs6000_emit_eqne (<MODE>mode, + operands[1], operands[2], operands[4]); + + if (GET_CODE (operands[5]) == SCRATCH) + operands[5] = gen_reg_rtx (<MODE>mode); +} + [(set (attr "length") + (if_then_else (match_test "operands[2] == const0_rtx") + (const_string "8") + (const_string "12")))]) + +(define_insn_and_split "*minus_eq_<mode>" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (minus:P (match_operand:P 3 "gpc_reg_operand" "r") + (eq:P (match_operand:P 1 "gpc_reg_operand" "r") + (match_operand:P 2 "scc_eq_operand" "<scc_eq_op2>")))) + (clobber (match_scratch:P 4 "=r")) + (clobber (match_scratch:P 5 "=r")) + (clobber (reg:P CA_REGNO))] + "" + "#" + "" + [(parallel [(set (match_dup 5) + (plus:P (match_dup 4) + (const_int -1))) + (set (reg:P CA_REGNO) + (ne:P (match_dup 4) + (const_int 0)))]) + (parallel [(set (match_dup 0) + (plus:P (plus:P (match_dup 3) + (reg:P CA_REGNO)) + (const_int -1))) + (clobber (reg:P CA_REGNO))])] +{ + operands[4] = rs6000_emit_eqne (<MODE>mode, + operands[1], operands[2], operands[4]); + + if (GET_CODE (operands[5]) == SCRATCH) + operands[5] = gen_reg_rtx (<MODE>mode); +} + [(set (attr "length") + (if_then_else (match_test "operands[2] == const0_rtx") + (const_string "8") + (const_string "12")))]) + +(define_insn_and_split "*minus_ne_<mode>" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (minus:P (match_operand:P 3 "gpc_reg_operand" "r") + (ne:P (match_operand:P 1 "gpc_reg_operand" "r") + (match_operand:P 2 "scc_eq_operand" "<scc_eq_op2>")))) + (clobber (match_scratch:P 4 "=r")) + (clobber (match_scratch:P 5 "=r")) + (clobber (reg:P CA_REGNO))] + "" + "#" + "" + [(parallel [(set (match_dup 5) + (neg:P (match_dup 4))) + (set (reg:P CA_REGNO) + (eq:P (match_dup 4) + (const_int 0)))]) + (parallel [(set (match_dup 0) + (plus:P (plus:P (match_dup 3) + (reg:P CA_REGNO)) + (const_int -1))) + (clobber (reg:P CA_REGNO))])] +{ + operands[4] = rs6000_emit_eqne (<MODE>mode, + operands[1], operands[2], operands[4]); + + if (GET_CODE (operands[5]) == SCRATCH) + operands[5] = gen_reg_rtx (<MODE>mode); +} + [(set (attr "length") + (if_then_else (match_test "operands[2] == const0_rtx") + (const_string "8") + (const_string "12")))]) + +(define_insn_and_split "*eqsi3_ext<mode>" + [(set (match_operand:EXTSI 0 "gpc_reg_operand" "=r") + (eq:EXTSI (match_operand:SI 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "scc_eq_operand" "rKLI"))) + (clobber (match_scratch:SI 3 "=r")) + (clobber (match_scratch:SI 4 "=r"))] + "" + "#" + "" + [(set (match_dup 4) + (clz:SI (match_dup 3))) + (set (match_dup 0) + (zero_extend:EXTSI + (lshiftrt:SI (match_dup 4) + (const_int 5))))] +{ + operands[3] = rs6000_emit_eqne (SImode, + operands[1], operands[2], operands[3]); + + if (GET_CODE (operands[4]) == SCRATCH) + operands[4] = gen_reg_rtx (SImode); +} + [(set (attr "length") + (if_then_else (match_test "operands[2] == const0_rtx") + (const_string "8") + (const_string "12")))]) + +(define_insn_and_split "*nesi3_ext<mode>" + [(set (match_operand:EXTSI 0 "gpc_reg_operand" "=r") + (ne:EXTSI (match_operand:SI 1 "gpc_reg_operand" "r") + (match_operand:SI 2 "scc_eq_operand" "rKLI"))) + (clobber (match_scratch:SI 3 "=r")) + (clobber (match_scratch:SI 4 "=r")) + (clobber (match_scratch:EXTSI 5 "=r"))] + "" + "#" + "" + [(set (match_dup 4) + (clz:SI (match_dup 3))) + (set (match_dup 5) + (zero_extend:EXTSI + (lshiftrt:SI (match_dup 4) + (const_int 5)))) + (set (match_dup 0) + (xor:EXTSI (match_dup 5) + (const_int 1)))] +{ + operands[3] = rs6000_emit_eqne (SImode, + operands[1], operands[2], operands[3]); + + if (GET_CODE (operands[4]) == SCRATCH) + operands[4] = gen_reg_rtx (SImode); + if (GET_CODE (operands[5]) == SCRATCH) + operands[5] = gen_reg_rtx (<MODE>mode); +} + [(set (attr "length") + (if_then_else (match_test "operands[2] == const0_rtx") + (const_string "12") + (const_string "16")))]) + +;; Define both directions of branch and return. If we need a reload +;; register, we'd rather use CR0 since it is much easier to copy a +;; register CC value to there. + +(define_insn "" + [(set (pc) + (if_then_else (match_operator 1 "branch_comparison_operator" + [(match_operand 2 + "cc_reg_operand" "y") + (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "" + "* +{ + return output_cbranch (operands[1], \"%l0\", 0, insn); +}" + [(set_attr "type" "branch")]) + +(define_insn "" + [(set (pc) + (if_then_else (match_operator 0 "branch_comparison_operator" + [(match_operand 1 + "cc_reg_operand" "y") + (const_int 0)]) + (any_return) + (pc)))] + "<return_pred>" + "* +{ + return output_cbranch (operands[0], NULL, 0, insn); +}" + [(set_attr "type" "jmpreg") + (set_attr "length" "4")]) + +(define_insn "" + [(set (pc) + (if_then_else (match_operator 1 "branch_comparison_operator" + [(match_operand 2 + "cc_reg_operand" "y") + (const_int 0)]) + (pc) + (label_ref (match_operand 0 "" ""))))] + "" + "* +{ + return output_cbranch (operands[1], \"%l0\", 1, insn); +}" + [(set_attr "type" "branch")]) + +(define_insn "" + [(set (pc) + (if_then_else (match_operator 0 "branch_comparison_operator" + [(match_operand 1 + "cc_reg_operand" "y") + (const_int 0)]) + (pc) + (any_return)))] + "<return_pred>" + "* +{ + return output_cbranch (operands[0], NULL, 1, insn); +}" + [(set_attr "type" "jmpreg") + (set_attr "length" "4")]) + +;; Logic on condition register values. + +; This pattern matches things like +; (set (reg:CCEQ 68) (compare:CCEQ (ior:SI (gt:SI (reg:CCFP 68) (const_int 0)) +; (eq:SI (reg:CCFP 68) (const_int 0))) +; (const_int 1))) +; which are generated by the branch logic. +; Prefer destructive operations where BT = BB (for crXX BT,BA,BB) + +(define_insn "*cceq_ior_compare" + [(set (match_operand:CCEQ 0 "cc_reg_operand" "=y,?y") + (compare:CCEQ (match_operator:SI 1 "boolean_operator" + [(match_operator:SI 2 + "branch_positive_comparison_operator" + [(match_operand 3 + "cc_reg_operand" "y,y") + (const_int 0)]) + (match_operator:SI 4 + "branch_positive_comparison_operator" + [(match_operand 5 + "cc_reg_operand" "0,y") + (const_int 0)])]) + (const_int 1)))] + "" + "cr%q1 %E0,%j2,%j4" + [(set_attr "type" "cr_logical,delayed_cr")]) + +; Why is the constant -1 here, but 1 in the previous pattern? +; Because ~1 has all but the low bit set. +(define_insn "" + [(set (match_operand:CCEQ 0 "cc_reg_operand" "=y,?y") + (compare:CCEQ (match_operator:SI 1 "boolean_or_operator" + [(not:SI (match_operator:SI 2 + "branch_positive_comparison_operator" + [(match_operand 3 + "cc_reg_operand" "y,y") + (const_int 0)])) + (match_operator:SI 4 + "branch_positive_comparison_operator" + [(match_operand 5 + "cc_reg_operand" "0,y") + (const_int 0)])]) + (const_int -1)))] + "" + "cr%q1 %E0,%j2,%j4" + [(set_attr "type" "cr_logical,delayed_cr")]) + +(define_insn "*cceq_rev_compare" + [(set (match_operand:CCEQ 0 "cc_reg_operand" "=y,?y") + (compare:CCEQ (match_operator:SI 1 + "branch_positive_comparison_operator" + [(match_operand 2 + "cc_reg_operand" "0,y") + (const_int 0)]) + (const_int 0)))] + "" + "crnot %E0,%j1" + [(set_attr "type" "cr_logical,delayed_cr")]) + +;; If we are comparing the result of two comparisons, this can be done +;; using creqv or crxor. + +(define_insn_and_split "" + [(set (match_operand:CCEQ 0 "cc_reg_operand" "=y") + (compare:CCEQ (match_operator 1 "branch_comparison_operator" + [(match_operand 2 "cc_reg_operand" "y") + (const_int 0)]) + (match_operator 3 "branch_comparison_operator" + [(match_operand 4 "cc_reg_operand" "y") + (const_int 0)])))] + "" + "#" + "" + [(set (match_dup 0) (compare:CCEQ (xor:SI (match_dup 1) (match_dup 3)) + (match_dup 5)))] + " +{ + int positive_1, positive_2; + + positive_1 = branch_positive_comparison_operator (operands[1], + GET_MODE (operands[1])); + positive_2 = branch_positive_comparison_operator (operands[3], + GET_MODE (operands[3])); + + if (! positive_1) + operands[1] = gen_rtx_fmt_ee (rs6000_reverse_condition (GET_MODE (operands[2]), + GET_CODE (operands[1])), + SImode, + operands[2], const0_rtx); + else if (GET_MODE (operands[1]) != SImode) + operands[1] = gen_rtx_fmt_ee (GET_CODE (operands[1]), SImode, + operands[2], const0_rtx); + + if (! positive_2) + operands[3] = gen_rtx_fmt_ee (rs6000_reverse_condition (GET_MODE (operands[4]), + GET_CODE (operands[3])), + SImode, + operands[4], const0_rtx); + else if (GET_MODE (operands[3]) != SImode) + operands[3] = gen_rtx_fmt_ee (GET_CODE (operands[3]), SImode, + operands[4], const0_rtx); + + if (positive_1 == positive_2) + { + operands[1] = gen_rtx_NOT (SImode, operands[1]); + operands[5] = constm1_rtx; + } + else + { + operands[5] = const1_rtx; + } +}") + +;; Unconditional branch and return. + +(define_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "" + "b %l0" + [(set_attr "type" "branch")]) + +(define_insn "<return_str>return" + [(any_return)] + "<return_pred>" + "blr" + [(set_attr "type" "jmpreg")]) + +(define_expand "indirect_jump" + [(set (pc) (match_operand 0 "register_operand" ""))]) + +(define_insn "*indirect_jump<mode>" + [(set (pc) (match_operand:P 0 "register_operand" "c,*l"))] + "" + "@ + bctr + blr" + [(set_attr "type" "jmpreg")]) + +;; Table jump for switch statements: +(define_expand "tablejump" + [(use (match_operand 0 "" "")) + (use (label_ref (match_operand 1 "" "")))] + "" + " +{ + if (TARGET_32BIT) + emit_jump_insn (gen_tablejumpsi (operands[0], operands[1])); + else + emit_jump_insn (gen_tablejumpdi (operands[0], operands[1])); + DONE; +}") + +(define_expand "tablejumpsi" + [(set (match_dup 3) + (plus:SI (match_operand:SI 0 "" "") + (match_dup 2))) + (parallel [(set (pc) (match_dup 3)) + (use (label_ref (match_operand 1 "" "")))])] + "TARGET_32BIT" + " +{ operands[0] = force_reg (SImode, operands[0]); + operands[2] = force_reg (SImode, gen_rtx_LABEL_REF (SImode, operands[1])); + operands[3] = gen_reg_rtx (SImode); +}") + +(define_expand "tablejumpdi" + [(set (match_dup 4) + (sign_extend:DI (match_operand:SI 0 "lwa_operand" ""))) + (set (match_dup 3) + (plus:DI (match_dup 4) + (match_dup 2))) + (parallel [(set (pc) (match_dup 3)) + (use (label_ref (match_operand 1 "" "")))])] + "TARGET_64BIT" + " +{ operands[2] = force_reg (DImode, gen_rtx_LABEL_REF (DImode, operands[1])); + operands[3] = gen_reg_rtx (DImode); + operands[4] = gen_reg_rtx (DImode); +}") + +(define_insn "*tablejump<mode>_internal1" + [(set (pc) + (match_operand:P 0 "register_operand" "c,*l")) + (use (label_ref (match_operand 1 "" "")))] + "" + "@ + bctr + blr" + [(set_attr "type" "jmpreg")]) + +(define_insn "nop" + [(unspec [(const_int 0)] UNSPEC_NOP)] + "" + "nop") + +(define_insn "group_ending_nop" + [(unspec [(const_int 0)] UNSPEC_GRP_END_NOP)] + "" + "* +{ + if (rs6000_cpu_attr == CPU_POWER6) + return \"ori 1,1,0\"; + return \"ori 2,2,0\"; +}") + +;; Define the subtract-one-and-jump insns, starting with the template +;; so loop.c knows what to generate. + +(define_expand "doloop_end" + [(use (match_operand 0 "" "")) ; loop pseudo + (use (match_operand 1 "" ""))] ; label + "" + " +{ + if (TARGET_64BIT) + { + if (GET_MODE (operands[0]) != DImode) + FAIL; + emit_jump_insn (gen_ctrdi (operands[0], operands[1])); + } + else + { + if (GET_MODE (operands[0]) != SImode) + FAIL; + emit_jump_insn (gen_ctrsi (operands[0], operands[1])); + } + DONE; +}") + +(define_expand "ctr<mode>" + [(parallel [(set (pc) + (if_then_else (ne (match_operand:P 0 "register_operand" "") + (const_int 1)) + (label_ref (match_operand 1 "" "")) + (pc))) + (set (match_dup 0) + (plus:P (match_dup 0) + (const_int -1))) + (clobber (match_scratch:CC 2 "")) + (clobber (match_scratch:P 3 ""))])] + "" + "") + +;; We need to be able to do this for any operand, including MEM, or we +;; will cause reload to blow up since we don't allow output reloads on +;; JUMP_INSNs. +;; For the length attribute to be calculated correctly, the +;; label MUST be operand 0. +;; rs6000_legitimate_combined_insn prevents combine creating any of +;; the ctr<mode> insns. + +(define_insn "ctr<mode>_internal1" + [(set (pc) + (if_then_else (ne (match_operand:P 1 "register_operand" "c,*b,*b,*b") + (const_int 1)) + (label_ref (match_operand 0 "" "")) + (pc))) + (set (match_operand:P 2 "nonimmediate_operand" "=1,*r,m,*d*wi*c*l") + (plus:P (match_dup 1) + (const_int -1))) + (clobber (match_scratch:CC 3 "=X,&x,&x,&x")) + (clobber (match_scratch:P 4 "=X,X,&r,r"))] + "" + "* +{ + if (which_alternative != 0) + return \"#\"; + else if (get_attr_length (insn) == 4) + return \"bdnz %l0\"; + else + return \"bdz $+8\;b %l0\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "*,16,20,20")]) + +(define_insn "ctr<mode>_internal2" + [(set (pc) + (if_then_else (ne (match_operand:P 1 "register_operand" "c,*b,*b,*b") + (const_int 1)) + (pc) + (label_ref (match_operand 0 "" "")))) + (set (match_operand:P 2 "nonimmediate_operand" "=1,*r,m,*d*wi*c*l") + (plus:P (match_dup 1) + (const_int -1))) + (clobber (match_scratch:CC 3 "=X,&x,&x,&x")) + (clobber (match_scratch:P 4 "=X,X,&r,r"))] + "" + "* +{ + if (which_alternative != 0) + return \"#\"; + else if (get_attr_length (insn) == 4) + return \"bdz %l0\"; + else + return \"bdnz $+8\;b %l0\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "*,16,20,20")]) + +;; Similar but use EQ + +(define_insn "ctr<mode>_internal3" + [(set (pc) + (if_then_else (eq (match_operand:P 1 "register_operand" "c,*b,*b,*b") + (const_int 1)) + (label_ref (match_operand 0 "" "")) + (pc))) + (set (match_operand:P 2 "nonimmediate_operand" "=1,*r,m,*d*wi*c*l") + (plus:P (match_dup 1) + (const_int -1))) + (clobber (match_scratch:CC 3 "=X,&x,&x,&x")) + (clobber (match_scratch:P 4 "=X,X,&r,r"))] + "" + "* +{ + if (which_alternative != 0) + return \"#\"; + else if (get_attr_length (insn) == 4) + return \"bdz %l0\"; + else + return \"bdnz $+8\;b %l0\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "*,16,20,20")]) + +(define_insn "ctr<mode>_internal4" + [(set (pc) + (if_then_else (eq (match_operand:P 1 "register_operand" "c,*b,*b,*b") + (const_int 1)) + (pc) + (label_ref (match_operand 0 "" "")))) + (set (match_operand:P 2 "nonimmediate_operand" "=1,*r,m,*d*wi*c*l") + (plus:P (match_dup 1) + (const_int -1))) + (clobber (match_scratch:CC 3 "=X,&x,&x,&x")) + (clobber (match_scratch:P 4 "=X,X,&r,r"))] + "" + "* +{ + if (which_alternative != 0) + return \"#\"; + else if (get_attr_length (insn) == 4) + return \"bdnz %l0\"; + else + return \"bdz $+8\;b %l0\"; +}" + [(set_attr "type" "branch") + (set_attr "length" "*,16,20,20")]) + +;; Now the splitters if we could not allocate the CTR register + +(define_split + [(set (pc) + (if_then_else (match_operator 2 "comparison_operator" + [(match_operand:P 1 "gpc_reg_operand" "") + (const_int 1)]) + (match_operand 5 "" "") + (match_operand 6 "" ""))) + (set (match_operand:P 0 "int_reg_operand" "") + (plus:P (match_dup 1) (const_int -1))) + (clobber (match_scratch:CC 3 "")) + (clobber (match_scratch:P 4 ""))] + "reload_completed" + [(set (match_dup 3) + (compare:CC (match_dup 1) + (const_int 1))) + (set (match_dup 0) + (plus:P (match_dup 1) + (const_int -1))) + (set (pc) (if_then_else (match_dup 7) + (match_dup 5) + (match_dup 6)))] + " +{ operands[7] = gen_rtx_fmt_ee (GET_CODE (operands[2]), VOIDmode, + operands[3], const0_rtx); }") + +(define_split + [(set (pc) + (if_then_else (match_operator 2 "comparison_operator" + [(match_operand:P 1 "gpc_reg_operand" "") + (const_int 1)]) + (match_operand 5 "" "") + (match_operand 6 "" ""))) + (set (match_operand:P 0 "nonimmediate_operand" "") + (plus:P (match_dup 1) (const_int -1))) + (clobber (match_scratch:CC 3 "")) + (clobber (match_scratch:P 4 ""))] + "reload_completed && ! gpc_reg_operand (operands[0], SImode)" + [(set (match_dup 3) + (compare:CC (match_dup 1) + (const_int 1))) + (set (match_dup 4) + (plus:P (match_dup 1) + (const_int -1))) + (set (match_dup 0) + (match_dup 4)) + (set (pc) (if_then_else (match_dup 7) + (match_dup 5) + (match_dup 6)))] + " +{ operands[7] = gen_rtx_fmt_ee (GET_CODE (operands[2]), VOIDmode, + operands[3], const0_rtx); }") + +(define_insn "trap" + [(trap_if (const_int 1) (const_int 0))] + "" + "trap" + [(set_attr "type" "trap")]) + +(define_expand "ctrap<mode>4" + [(trap_if (match_operator 0 "ordered_comparison_operator" + [(match_operand:GPR 1 "register_operand") + (match_operand:GPR 2 "reg_or_short_operand")]) + (match_operand 3 "zero_constant" ""))] + "" + "") + +(define_insn "" + [(trap_if (match_operator 0 "ordered_comparison_operator" + [(match_operand:GPR 1 "register_operand" "r") + (match_operand:GPR 2 "reg_or_short_operand" "rI")]) + (const_int 0))] + "" + "t<wd>%V0%I2 %1,%2" + [(set_attr "type" "trap")]) + +;; Insns related to generating the function prologue and epilogue. + +(define_expand "prologue" + [(use (const_int 0))] + "" +{ + rs6000_emit_prologue (); + if (!TARGET_SCHED_PROLOG) + emit_insn (gen_blockage ()); + DONE; +}) + +(define_insn "*movesi_from_cr_one" + [(match_parallel 0 "mfcr_operation" + [(set (match_operand:SI 1 "gpc_reg_operand" "=r") + (unspec:SI [(match_operand:CC 2 "cc_reg_operand" "y") + (match_operand 3 "immediate_operand" "n")] + UNSPEC_MOVESI_FROM_CR))])] + "TARGET_MFCRF" + "* +{ + int mask = 0; + int i; + for (i = 0; i < XVECLEN (operands[0], 0); i++) + { + mask = INTVAL (XVECEXP (SET_SRC (XVECEXP (operands[0], 0, i)), 0, 1)); + operands[4] = GEN_INT (mask); + output_asm_insn (\"mfcr %1,%4\", operands); + } + return \"\"; +}" + [(set_attr "type" "mfcrf")]) + +(define_insn "movesi_from_cr" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (unspec:SI [(reg:CC CR0_REGNO) (reg:CC CR1_REGNO) + (reg:CC CR2_REGNO) (reg:CC CR3_REGNO) + (reg:CC CR4_REGNO) (reg:CC CR5_REGNO) + (reg:CC CR6_REGNO) (reg:CC CR7_REGNO)] + UNSPEC_MOVESI_FROM_CR))] + "" + "mfcr %0" + [(set_attr "type" "mfcr")]) + +(define_insn "*crsave" + [(match_parallel 0 "crsave_operation" + [(set (match_operand:SI 1 "memory_operand" "=m") + (match_operand:SI 2 "gpc_reg_operand" "r"))])] + "" + "stw %2,%1" + [(set_attr "type" "store")]) + +(define_insn "*stmw" + [(match_parallel 0 "stmw_operation" + [(set (match_operand:SI 1 "memory_operand" "=m") + (match_operand:SI 2 "gpc_reg_operand" "r"))])] + "TARGET_MULTIPLE" + "stmw %2,%1" + [(set_attr "type" "store") + (set_attr "update" "yes") + (set_attr "indexed" "yes")]) + +; The following comment applies to: +; save_gpregs_* +; save_fpregs_* +; restore_gpregs* +; return_and_restore_gpregs* +; return_and_restore_fpregs* +; return_and_restore_fpregs_aix* +; +; The out-of-line save / restore functions expects one input argument. +; Since those are not standard call_insn's, we must avoid using +; MATCH_OPERAND for that argument. That way the register rename +; optimization will not try to rename this register. +; Each pattern is repeated for each possible register number used in +; various ABIs (r11, r1, and for some functions r12) + +(define_insn "*save_gpregs_<mode>_r11" + [(match_parallel 0 "any_parallel_operand" + [(clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 11)) + (set (match_operand:P 2 "memory_operand" "=m") + (match_operand:P 3 "gpc_reg_operand" "r"))])] + "" + "bl %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*save_gpregs_<mode>_r12" + [(match_parallel 0 "any_parallel_operand" + [(clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 12)) + (set (match_operand:P 2 "memory_operand" "=m") + (match_operand:P 3 "gpc_reg_operand" "r"))])] + "" + "bl %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*save_gpregs_<mode>_r1" + [(match_parallel 0 "any_parallel_operand" + [(clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 1)) + (set (match_operand:P 2 "memory_operand" "=m") + (match_operand:P 3 "gpc_reg_operand" "r"))])] + "" + "bl %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*save_fpregs_<mode>_r11" + [(match_parallel 0 "any_parallel_operand" + [(clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 11)) + (set (match_operand:DF 2 "memory_operand" "=m") + (match_operand:DF 3 "gpc_reg_operand" "d"))])] + "" + "bl %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*save_fpregs_<mode>_r12" + [(match_parallel 0 "any_parallel_operand" + [(clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 12)) + (set (match_operand:DF 2 "memory_operand" "=m") + (match_operand:DF 3 "gpc_reg_operand" "d"))])] + "" + "bl %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*save_fpregs_<mode>_r1" + [(match_parallel 0 "any_parallel_operand" + [(clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 1)) + (set (match_operand:DF 2 "memory_operand" "=m") + (match_operand:DF 3 "gpc_reg_operand" "d"))])] + "" + "bl %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +; This is to explain that changes to the stack pointer should +; not be moved over loads from or stores to stack memory. +(define_insn "stack_tie" + [(match_parallel 0 "tie_operand" + [(set (mem:BLK (reg 1)) (const_int 0))])] + "" + "" + [(set_attr "length" "0")]) + +; Some 32-bit ABIs do not have a red zone, so the stack deallocation has to +; stay behind all restores from the stack, it cannot be reordered to before +; one. See PR77687. This insn is an add or mr, and a stack_tie on the +; operands of that. +(define_insn "stack_restore_tie" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r,r") + (plus:SI (match_operand:SI 1 "gpc_reg_operand" "r,r") + (match_operand:SI 2 "reg_or_cint_operand" "O,rI"))) + (set (mem:BLK (match_dup 0)) (const_int 0)) + (set (mem:BLK (match_dup 1)) (const_int 0))] + "TARGET_32BIT" + "@ + mr %0,%1 + add%I2 %0,%1,%2" + [(set_attr "type" "*,add")]) + +(define_expand "epilogue" + [(use (const_int 0))] + "" +{ + if (!TARGET_SCHED_PROLOG) + emit_insn (gen_blockage ()); + rs6000_emit_epilogue (FALSE); + DONE; +}) + +; On some processors, doing the mtcrf one CC register at a time is +; faster (like on the 604e). On others, doing them all at once is +; faster; for instance, on the 601 and 750. + +(define_expand "movsi_to_cr_one" + [(set (match_operand:CC 0 "cc_reg_operand" "") + (unspec:CC [(match_operand:SI 1 "gpc_reg_operand" "") + (match_dup 2)] UNSPEC_MOVESI_TO_CR))] + "" + "operands[2] = GEN_INT (1 << (75 - REGNO (operands[0])));") + +(define_insn "*movsi_to_cr" + [(match_parallel 0 "mtcrf_operation" + [(set (match_operand:CC 1 "cc_reg_operand" "=y") + (unspec:CC [(match_operand:SI 2 "gpc_reg_operand" "r") + (match_operand 3 "immediate_operand" "n")] + UNSPEC_MOVESI_TO_CR))])] + "" + "* +{ + int mask = 0; + int i; + for (i = 0; i < XVECLEN (operands[0], 0); i++) + mask |= INTVAL (XVECEXP (SET_SRC (XVECEXP (operands[0], 0, i)), 0, 1)); + operands[4] = GEN_INT (mask); + return \"mtcrf %4,%2\"; +}" + [(set_attr "type" "mtcr")]) + +(define_insn "*mtcrfsi" + [(set (match_operand:CC 0 "cc_reg_operand" "=y") + (unspec:CC [(match_operand:SI 1 "gpc_reg_operand" "r") + (match_operand 2 "immediate_operand" "n")] + UNSPEC_MOVESI_TO_CR))] + "GET_CODE (operands[0]) == REG + && CR_REGNO_P (REGNO (operands[0])) + && GET_CODE (operands[2]) == CONST_INT + && INTVAL (operands[2]) == 1 << (75 - REGNO (operands[0]))" + "mtcrf %R0,%1" + [(set_attr "type" "mtcr")]) + +; The load-multiple instructions have similar properties. +; Note that "load_multiple" is a name known to the machine-independent +; code that actually corresponds to the PowerPC load-string. + +(define_insn "*lmw" + [(match_parallel 0 "lmw_operation" + [(set (match_operand:SI 1 "gpc_reg_operand" "=r") + (match_operand:SI 2 "memory_operand" "m"))])] + "TARGET_MULTIPLE" + "lmw %1,%2" + [(set_attr "type" "load") + (set_attr "update" "yes") + (set_attr "indexed" "yes") + (set_attr "cell_micro" "always")]) + +; FIXME: This would probably be somewhat simpler if the Cygnus sibcall +; stuff was in GCC. Oh, and "any_parallel_operand" is a bit flexible... + +; The following comment applies to: +; save_gpregs_* +; save_fpregs_* +; restore_gpregs* +; return_and_restore_gpregs* +; return_and_restore_fpregs* +; return_and_restore_fpregs_aix* +; +; The out-of-line save / restore functions expects one input argument. +; Since those are not standard call_insn's, we must avoid using +; MATCH_OPERAND for that argument. That way the register rename +; optimization will not try to rename this register. +; Each pattern is repeated for each possible register number used in +; various ABIs (r11, r1, and for some functions r12) + +(define_insn "*restore_gpregs_<mode>_r11" + [(match_parallel 0 "any_parallel_operand" + [(clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 11)) + (set (match_operand:P 2 "gpc_reg_operand" "=r") + (match_operand:P 3 "memory_operand" "m"))])] + "" + "bl %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*restore_gpregs_<mode>_r12" + [(match_parallel 0 "any_parallel_operand" + [(clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 12)) + (set (match_operand:P 2 "gpc_reg_operand" "=r") + (match_operand:P 3 "memory_operand" "m"))])] + "" + "bl %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*restore_gpregs_<mode>_r1" + [(match_parallel 0 "any_parallel_operand" + [(clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 1)) + (set (match_operand:P 2 "gpc_reg_operand" "=r") + (match_operand:P 3 "memory_operand" "m"))])] + "" + "bl %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*return_and_restore_gpregs_<mode>_r11" + [(match_parallel 0 "any_parallel_operand" + [(return) + (clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 11)) + (set (match_operand:P 2 "gpc_reg_operand" "=r") + (match_operand:P 3 "memory_operand" "m"))])] + "" + "b %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*return_and_restore_gpregs_<mode>_r12" + [(match_parallel 0 "any_parallel_operand" + [(return) + (clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 12)) + (set (match_operand:P 2 "gpc_reg_operand" "=r") + (match_operand:P 3 "memory_operand" "m"))])] + "" + "b %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*return_and_restore_gpregs_<mode>_r1" + [(match_parallel 0 "any_parallel_operand" + [(return) + (clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 1)) + (set (match_operand:P 2 "gpc_reg_operand" "=r") + (match_operand:P 3 "memory_operand" "m"))])] + "" + "b %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*return_and_restore_fpregs_<mode>_r11" + [(match_parallel 0 "any_parallel_operand" + [(return) + (clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 11)) + (set (match_operand:DF 2 "gpc_reg_operand" "=d") + (match_operand:DF 3 "memory_operand" "m"))])] + "" + "b %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*return_and_restore_fpregs_<mode>_r12" + [(match_parallel 0 "any_parallel_operand" + [(return) + (clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 12)) + (set (match_operand:DF 2 "gpc_reg_operand" "=d") + (match_operand:DF 3 "memory_operand" "m"))])] + "" + "b %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*return_and_restore_fpregs_<mode>_r1" + [(match_parallel 0 "any_parallel_operand" + [(return) + (clobber (reg:P LR_REGNO)) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 1)) + (set (match_operand:DF 2 "gpc_reg_operand" "=d") + (match_operand:DF 3 "memory_operand" "m"))])] + "" + "b %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*return_and_restore_fpregs_aix_<mode>_r11" + [(match_parallel 0 "any_parallel_operand" + [(return) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 11)) + (set (match_operand:DF 2 "gpc_reg_operand" "=d") + (match_operand:DF 3 "memory_operand" "m"))])] + "" + "b %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +(define_insn "*return_and_restore_fpregs_aix_<mode>_r1" + [(match_parallel 0 "any_parallel_operand" + [(return) + (use (match_operand:P 1 "symbol_ref_operand" "s")) + (use (reg:P 1)) + (set (match_operand:DF 2 "gpc_reg_operand" "=d") + (match_operand:DF 3 "memory_operand" "m"))])] + "" + "b %1" + [(set_attr "type" "branch") + (set_attr "length" "4")]) + +; This is used in compiling the unwind routines. +(define_expand "eh_return" + [(use (match_operand 0 "general_operand" ""))] + "" + " +{ + if (TARGET_32BIT) + emit_insn (gen_eh_set_lr_si (operands[0])); + else + emit_insn (gen_eh_set_lr_di (operands[0])); + DONE; +}") + +; We can't expand this before we know where the link register is stored. +(define_insn "eh_set_lr_<mode>" + [(unspec_volatile [(match_operand:P 0 "register_operand" "r")] + UNSPECV_EH_RR) + (clobber (match_scratch:P 1 "=&b"))] + "" + "#") + +(define_split + [(unspec_volatile [(match_operand 0 "register_operand" "")] UNSPECV_EH_RR) + (clobber (match_scratch 1 ""))] + "reload_completed" + [(const_int 0)] + " +{ + rs6000_emit_eh_reg_restore (operands[0], operands[1]); + DONE; +}") + +(define_insn "prefetch" + [(prefetch (match_operand 0 "indexed_or_indirect_address" "a") + (match_operand:SI 1 "const_int_operand" "n") + (match_operand:SI 2 "const_int_operand" "n"))] + "" + "* +{ + if (GET_CODE (operands[0]) == REG) + return INTVAL (operands[1]) ? \"dcbtst 0,%0\" : \"dcbt 0,%0\"; + return INTVAL (operands[1]) ? \"dcbtst %a0\" : \"dcbt %a0\"; +}" + [(set_attr "type" "load")]) + +;; Handle -fsplit-stack. + +(define_expand "split_stack_prologue" + [(const_int 0)] + "" +{ + rs6000_expand_split_stack_prologue (); + DONE; +}) + +(define_expand "load_split_stack_limit" + [(set (match_operand 0) + (unspec [(const_int 0)] UNSPEC_STACK_CHECK))] + "" +{ + emit_insn (gen_rtx_SET (operands[0], + gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, const0_rtx), + UNSPEC_STACK_CHECK))); + DONE; +}) + +(define_insn "load_split_stack_limit_di" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (unspec:DI [(const_int 0)] UNSPEC_STACK_CHECK))] + "TARGET_64BIT" + "ld %0,-0x7040(13)" + [(set_attr "type" "load") + (set_attr "update" "no") + (set_attr "indexed" "no")]) + +(define_insn "load_split_stack_limit_si" + [(set (match_operand:SI 0 "gpc_reg_operand" "=r") + (unspec:SI [(const_int 0)] UNSPEC_STACK_CHECK))] + "!TARGET_64BIT" + "lwz %0,-0x7020(2)" + [(set_attr "type" "load") + (set_attr "update" "no") + (set_attr "indexed" "no")]) + +;; A return instruction which the middle-end doesn't see. +;; Use r0 to stop regrename twiddling with lr restore insns emitted +;; after the call to __morestack. +(define_insn "split_stack_return" + [(unspec_volatile [(use (reg:SI 0))] UNSPECV_SPLIT_STACK_RETURN)] + "" + "blr" + [(set_attr "type" "jmpreg")]) + +;; If there are operand 0 bytes available on the stack, jump to +;; operand 1. +(define_expand "split_stack_space_check" + [(set (match_dup 2) + (unspec [(const_int 0)] UNSPEC_STACK_CHECK)) + (set (match_dup 3) + (minus (reg STACK_POINTER_REGNUM) + (match_operand 0))) + (set (match_dup 4) (compare:CCUNS (match_dup 3) (match_dup 2))) + (set (pc) (if_then_else + (geu (match_dup 4) (const_int 0)) + (label_ref (match_operand 1)) + (pc)))] + "" +{ + rs6000_split_stack_space_check (operands[0], operands[1]); + DONE; +}) + +(define_insn "bpermd_<mode>" + [(set (match_operand:P 0 "gpc_reg_operand" "=r") + (unspec:P [(match_operand:P 1 "gpc_reg_operand" "r") + (match_operand:P 2 "gpc_reg_operand" "r")] UNSPEC_BPERM))] + "TARGET_POPCNTD" + "bpermd %0,%1,%2" + [(set_attr "type" "popcnt")]) + + +;; Builtin fma support. Handle +;; Note that the conditions for expansion are in the FMA_F iterator. + +(define_expand "fma<mode>4" + [(set (match_operand:FMA_F 0 "gpc_reg_operand" "") + (fma:FMA_F + (match_operand:FMA_F 1 "gpc_reg_operand" "") + (match_operand:FMA_F 2 "gpc_reg_operand" "") + (match_operand:FMA_F 3 "gpc_reg_operand" "")))] + "" + "") + +(define_insn "*fma<mode>4_fpr" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv2>,<Fv2>") + (fma:SFDF + (match_operand:SFDF 1 "gpc_reg_operand" "%<Ff>,<Fv2>,<Fv2>") + (match_operand:SFDF 2 "gpc_reg_operand" "<Ff>,<Fv2>,0") + (match_operand:SFDF 3 "gpc_reg_operand" "<Ff>,0,<Fv2>")))] + "TARGET_<MODE>_FPR" + "@ + fmadd<Ftrad> %0,%1,%2,%3 + xsmadda<Fvsx> %x0,%x1,%x2 + xsmaddm<Fvsx> %x0,%x1,%x3" + [(set_attr "type" "fp") + (set_attr "fp_type" "fp_maddsub_<Fs>")]) + +; Altivec only has fma and nfms. +(define_expand "fms<mode>4" + [(set (match_operand:FMA_F 0 "gpc_reg_operand" "") + (fma:FMA_F + (match_operand:FMA_F 1 "gpc_reg_operand" "") + (match_operand:FMA_F 2 "gpc_reg_operand" "") + (neg:FMA_F (match_operand:FMA_F 3 "gpc_reg_operand" ""))))] + "!VECTOR_UNIT_ALTIVEC_P (<MODE>mode)" + "") + +(define_insn "*fms<mode>4_fpr" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv2>,<Fv2>") + (fma:SFDF + (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv2>,<Fv2>") + (match_operand:SFDF 2 "gpc_reg_operand" "<Ff>,<Fv2>,0") + (neg:SFDF (match_operand:SFDF 3 "gpc_reg_operand" "<Ff>,0,<Fv2>"))))] + "TARGET_<MODE>_FPR" + "@ + fmsub<Ftrad> %0,%1,%2,%3 + xsmsuba<Fvsx> %x0,%x1,%x2 + xsmsubm<Fvsx> %x0,%x1,%x3" + [(set_attr "type" "fp") + (set_attr "fp_type" "fp_maddsub_<Fs>")]) + +;; If signed zeros are ignored, -(a * b - c) = -a * b + c. +(define_expand "fnma<mode>4" + [(set (match_operand:FMA_F 0 "gpc_reg_operand" "") + (neg:FMA_F + (fma:FMA_F + (match_operand:FMA_F 1 "gpc_reg_operand" "") + (match_operand:FMA_F 2 "gpc_reg_operand" "") + (neg:FMA_F (match_operand:FMA_F 3 "gpc_reg_operand" "")))))] + "!HONOR_SIGNED_ZEROS (<MODE>mode)" + "") + +;; If signed zeros are ignored, -(a * b + c) = -a * b - c. +(define_expand "fnms<mode>4" + [(set (match_operand:FMA_F 0 "gpc_reg_operand" "") + (neg:FMA_F + (fma:FMA_F + (match_operand:FMA_F 1 "gpc_reg_operand" "") + (match_operand:FMA_F 2 "gpc_reg_operand" "") + (match_operand:FMA_F 3 "gpc_reg_operand" ""))))] + "!HONOR_SIGNED_ZEROS (<MODE>mode) && !VECTOR_UNIT_ALTIVEC_P (<MODE>mode)" + "") + +; Not an official optab name, but used from builtins. +(define_expand "nfma<mode>4" + [(set (match_operand:FMA_F 0 "gpc_reg_operand" "") + (neg:FMA_F + (fma:FMA_F + (match_operand:FMA_F 1 "gpc_reg_operand" "") + (match_operand:FMA_F 2 "gpc_reg_operand" "") + (match_operand:FMA_F 3 "gpc_reg_operand" ""))))] + "!VECTOR_UNIT_ALTIVEC_P (<MODE>mode)" + "") + +(define_insn "*nfma<mode>4_fpr" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv2>,<Fv2>") + (neg:SFDF + (fma:SFDF + (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv2>,<Fv2>") + (match_operand:SFDF 2 "gpc_reg_operand" "<Ff>,<Fv2>,0") + (match_operand:SFDF 3 "gpc_reg_operand" "<Ff>,0,<Fv2>"))))] + "TARGET_<MODE>_FPR" + "@ + fnmadd<Ftrad> %0,%1,%2,%3 + xsnmadda<Fvsx> %x0,%x1,%x2 + xsnmaddm<Fvsx> %x0,%x1,%x3" + [(set_attr "type" "fp") + (set_attr "fp_type" "fp_maddsub_<Fs>")]) + +; Not an official optab name, but used from builtins. +(define_expand "nfms<mode>4" + [(set (match_operand:FMA_F 0 "gpc_reg_operand" "") + (neg:FMA_F + (fma:FMA_F + (match_operand:FMA_F 1 "gpc_reg_operand" "") + (match_operand:FMA_F 2 "gpc_reg_operand" "") + (neg:FMA_F (match_operand:FMA_F 3 "gpc_reg_operand" "")))))] + "" + "") + +(define_insn "*nfmssf4_fpr" + [(set (match_operand:SFDF 0 "gpc_reg_operand" "=<Ff>,<Fv2>,<Fv2>") + (neg:SFDF + (fma:SFDF + (match_operand:SFDF 1 "gpc_reg_operand" "<Ff>,<Fv2>,<Fv2>") + (match_operand:SFDF 2 "gpc_reg_operand" "<Ff>,<Fv2>,0") + (neg:SFDF + (match_operand:SFDF 3 "gpc_reg_operand" "<Ff>,0,<Fv2>")))))] + "TARGET_<MODE>_FPR" + "@ + fnmsub<Ftrad> %0,%1,%2,%3 + xsnmsuba<Fvsx> %x0,%x1,%x2 + xsnmsubm<Fvsx> %x0,%x1,%x3" + [(set_attr "type" "fp") + (set_attr "fp_type" "fp_maddsub_<Fs>")]) + + +(define_expand "rs6000_get_timebase" + [(use (match_operand:DI 0 "gpc_reg_operand" ""))] + "" +{ + if (TARGET_POWERPC64) + emit_insn (gen_rs6000_mftb_di (operands[0])); + else + emit_insn (gen_rs6000_get_timebase_ppc32 (operands[0])); + DONE; +}) + +(define_insn "rs6000_get_timebase_ppc32" + [(set (match_operand:DI 0 "gpc_reg_operand" "=r") + (unspec_volatile:DI [(const_int 0)] UNSPECV_MFTB)) + (clobber (match_scratch:SI 1 "=r")) + (clobber (match_scratch:CC 2 "=y"))] + "!TARGET_POWERPC64" +{ + if (WORDS_BIG_ENDIAN) + if (TARGET_MFCRF) + { + return "mfspr %0,269\;" + "mfspr %L0,268\;" + "mfspr %1,269\;" + "cmpw %2,%0,%1\;" + "bne- %2,$-16"; + } + else + { + return "mftbu %0\;" + "mftb %L0\;" + "mftbu %1\;" + "cmpw %2,%0,%1\;" + "bne- %2,$-16"; + } + else + if (TARGET_MFCRF) + { + return "mfspr %L0,269\;" + "mfspr %0,268\;" + "mfspr %1,269\;" + "cmpw %2,%L0,%1\;" + "bne- %2,$-16"; + } + else + { + return "mftbu %L0\;" + "mftb %0\;" + "mftbu %1\;" + "cmpw %2,%L0,%1\;" + "bne- %2,$-16"; + } +} + [(set_attr "length" "20")]) + +(define_insn "rs6000_mftb_<mode>" + [(set (match_operand:GPR 0 "gpc_reg_operand" "=r") + (unspec_volatile:GPR [(const_int 0)] UNSPECV_MFTB))] + "" +{ + if (TARGET_MFCRF) + return "mfspr %0,268"; + else + return "mftb %0"; +}) + + +(define_insn "rs6000_mffs" + [(set (match_operand:DF 0 "gpc_reg_operand" "=d") + (unspec_volatile:DF [(const_int 0)] UNSPECV_MFFS))] + "TARGET_HARD_FLOAT && TARGET_FPRS" + "mffs %0") + +(define_insn "rs6000_mtfsf" + [(unspec_volatile [(match_operand:SI 0 "const_int_operand" "i") + (match_operand:DF 1 "gpc_reg_operand" "d")] + UNSPECV_MTFSF)] + "TARGET_HARD_FLOAT && TARGET_FPRS" + "mtfsf %0,%1") + + +;; Power8 fusion support for fusing an addis instruction with a D-form load of +;; a GPR. The addis instruction must be adjacent to the load, and use the same +;; register that is being loaded. The fused ops must be physically adjacent. + +;; There are two parts to addis fusion. The support for fused TOCs occur +;; before register allocation, and is meant to reduce the lifetime for the +;; tempoary register that holds the ADDIS result. On Power8 GPR loads, we try +;; to use the register that is being load. The peephole2 then gathers any +;; other fused possibilities that it can find after register allocation. If +;; power9 fusion is selected, we also fuse floating point loads/stores. + +;; Fused TOC support: Replace simple GPR loads with a fused form. This is done +;; before register allocation, so that we can avoid allocating a temporary base +;; register that won't be used, and that we try to load into base registers, +;; and not register 0. If we can't get a fused GPR load, generate a P9 fusion +;; (addis followed by load) even on power8. + +(define_split + [(set (match_operand:INT1 0 "toc_fusion_or_p9_reg_operand" "") + (match_operand:INT1 1 "toc_fusion_mem_raw" ""))] + "TARGET_TOC_FUSION_INT && can_create_pseudo_p ()" + [(parallel [(set (match_dup 0) (match_dup 2)) + (unspec [(const_int 0)] UNSPEC_FUSION_ADDIS) + (use (match_dup 3)) + (clobber (scratch:DI))])] +{ + operands[2] = fusion_wrap_memory_address (operands[1]); + operands[3] = gen_rtx_REG (Pmode, TOC_REGISTER); +}) + +(define_insn "*toc_fusionload_<mode>" + [(set (match_operand:QHSI 0 "int_reg_operand" "=&b,??r") + (match_operand:QHSI 1 "toc_fusion_mem_wrapped" "wG,wG")) + (unspec [(const_int 0)] UNSPEC_FUSION_ADDIS) + (use (match_operand:DI 2 "base_reg_operand" "r,r")) + (clobber (match_scratch:DI 3 "=X,&b"))] + "TARGET_TOC_FUSION_INT" +{ + if (base_reg_operand (operands[0], <MODE>mode)) + return emit_fusion_gpr_load (operands[0], operands[1]); + + return emit_fusion_p9_load (operands[0], operands[1], operands[3]); +} + [(set_attr "type" "load") + (set_attr "length" "8")]) + +(define_insn "*toc_fusionload_di" + [(set (match_operand:DI 0 "int_reg_operand" "=&b,??r,?d") + (match_operand:DI 1 "toc_fusion_mem_wrapped" "wG,wG,wG")) + (unspec [(const_int 0)] UNSPEC_FUSION_ADDIS) + (use (match_operand:DI 2 "base_reg_operand" "r,r,r")) + (clobber (match_scratch:DI 3 "=X,&b,&b"))] + "TARGET_TOC_FUSION_INT && TARGET_POWERPC64 + && (MEM_P (operands[1]) || int_reg_operand (operands[0], DImode))" +{ + if (base_reg_operand (operands[0], DImode)) + return emit_fusion_gpr_load (operands[0], operands[1]); + + return emit_fusion_p9_load (operands[0], operands[1], operands[3]); +} + [(set_attr "type" "load") + (set_attr "length" "8")]) + + +;; Find cases where the addis that feeds into a load instruction is either used +;; once or is the same as the target register, and replace it with the fusion +;; insn + +(define_peephole2 + [(set (match_operand:P 0 "base_reg_operand" "") + (match_operand:P 1 "fusion_gpr_addis" "")) + (set (match_operand:INT1 2 "base_reg_operand" "") + (match_operand:INT1 3 "fusion_gpr_mem_load" ""))] + "TARGET_P8_FUSION + && fusion_gpr_load_p (operands[0], operands[1], operands[2], + operands[3])" + [(const_int 0)] +{ + expand_fusion_gpr_load (operands); + DONE; +}) + +;; Fusion insn, created by the define_peephole2 above (and eventually by +;; reload) + +(define_insn "fusion_gpr_load_<mode>" + [(set (match_operand:INT1 0 "base_reg_operand" "=b") + (unspec:INT1 [(match_operand:INT1 1 "fusion_addis_mem_combo_load" "wF")] + UNSPEC_FUSION_GPR))] + "TARGET_P8_FUSION" +{ + return emit_fusion_gpr_load (operands[0], operands[1]); +} + [(set_attr "type" "load") + (set_attr "length" "8")]) + + +;; ISA 3.0 (power9) fusion support +;; Merge addis with floating load/store to FPRs (or GPRs). +(define_peephole2 + [(set (match_operand:P 0 "base_reg_operand" "") + (match_operand:P 1 "fusion_gpr_addis" "")) + (set (match_operand:SFDF 2 "toc_fusion_or_p9_reg_operand" "") + (match_operand:SFDF 3 "fusion_offsettable_mem_operand" ""))] + "TARGET_P9_FUSION && peep2_reg_dead_p (2, operands[0]) + && fusion_p9_p (operands[0], operands[1], operands[2], operands[3])" + [(const_int 0)] +{ + expand_fusion_p9_load (operands); + DONE; +}) + +(define_peephole2 + [(set (match_operand:P 0 "base_reg_operand" "") + (match_operand:P 1 "fusion_gpr_addis" "")) + (set (match_operand:SFDF 2 "offsettable_mem_operand" "") + (match_operand:SFDF 3 "toc_fusion_or_p9_reg_operand" ""))] + "TARGET_P9_FUSION && peep2_reg_dead_p (2, operands[0]) + && fusion_p9_p (operands[0], operands[1], operands[2], operands[3]) + && !rtx_equal_p (operands[0], operands[3])" + [(const_int 0)] +{ + expand_fusion_p9_store (operands); + DONE; +}) + +(define_peephole2 + [(set (match_operand:SDI 0 "int_reg_operand" "") + (match_operand:SDI 1 "upper16_cint_operand" "")) + (set (match_dup 0) + (ior:SDI (match_dup 0) + (match_operand:SDI 2 "u_short_cint_operand" "")))] + "TARGET_P9_FUSION" + [(set (match_dup 0) + (unspec:SDI [(match_dup 1) + (match_dup 2)] UNSPEC_FUSION_P9))]) + +(define_peephole2 + [(set (match_operand:SDI 0 "int_reg_operand" "") + (match_operand:SDI 1 "upper16_cint_operand" "")) + (set (match_operand:SDI 2 "int_reg_operand" "") + (ior:SDI (match_dup 0) + (match_operand:SDI 3 "u_short_cint_operand" "")))] + "TARGET_P9_FUSION + && !rtx_equal_p (operands[0], operands[2]) + && peep2_reg_dead_p (2, operands[0])" + [(set (match_dup 2) + (unspec:SDI [(match_dup 1) + (match_dup 3)] UNSPEC_FUSION_P9))]) + +;; Fusion insns, created by the define_peephole2 above (and eventually by +;; reload). Because we want to eventually have secondary_reload generate +;; these, they have to have a single alternative that gives the register +;; classes. This means we need to have separate gpr/fpr/altivec versions. +(define_insn "fusion_gpr_<P:mode>_<GPR_FUSION:mode>_load" + [(set (match_operand:GPR_FUSION 0 "int_reg_operand" "=r") + (unspec:GPR_FUSION + [(match_operand:GPR_FUSION 1 "fusion_addis_mem_combo_load" "wF")] + UNSPEC_FUSION_P9)) + (clobber (match_operand:P 2 "base_reg_operand" "=b"))] + "TARGET_P9_FUSION" +{ + /* This insn is a secondary reload insn, which cannot have alternatives. + If we are not loading up register 0, use the power8 fusion instead. */ + if (base_reg_operand (operands[0], <GPR_FUSION:MODE>mode)) + return emit_fusion_gpr_load (operands[0], operands[1]); + + return emit_fusion_p9_load (operands[0], operands[1], operands[2]); +} + [(set_attr "type" "load") + (set_attr "length" "8")]) + +(define_insn "fusion_gpr_<P:mode>_<GPR_FUSION:mode>_store" + [(set (match_operand:GPR_FUSION 0 "fusion_addis_mem_combo_store" "=wF") + (unspec:GPR_FUSION + [(match_operand:GPR_FUSION 1 "int_reg_operand" "r")] + UNSPEC_FUSION_P9)) + (clobber (match_operand:P 2 "base_reg_operand" "=b"))] + "TARGET_P9_FUSION" +{ + return emit_fusion_p9_store (operands[0], operands[1], operands[2]); +} + [(set_attr "type" "store") + (set_attr "length" "8")]) + +(define_insn "fusion_vsx_<P:mode>_<FPR_FUSION:mode>_load" + [(set (match_operand:FPR_FUSION 0 "vsx_register_operand" "=dwb") + (unspec:FPR_FUSION + [(match_operand:FPR_FUSION 1 "fusion_addis_mem_combo_load" "wF")] + UNSPEC_FUSION_P9)) + (clobber (match_operand:P 2 "base_reg_operand" "=b"))] + "TARGET_P9_FUSION" +{ + return emit_fusion_p9_load (operands[0], operands[1], operands[2]); +} + [(set_attr "type" "fpload") + (set_attr "length" "8")]) + +(define_insn "fusion_vsx_<P:mode>_<FPR_FUSION:mode>_store" + [(set (match_operand:FPR_FUSION 0 "fusion_addis_mem_combo_store" "=wF") + (unspec:FPR_FUSION + [(match_operand:FPR_FUSION 1 "vsx_register_operand" "dwb")] + UNSPEC_FUSION_P9)) + (clobber (match_operand:P 2 "base_reg_operand" "=b"))] + "TARGET_P9_FUSION" +{ + return emit_fusion_p9_store (operands[0], operands[1], operands[2]); +} + [(set_attr "type" "fpstore") + (set_attr "length" "8")]) + +(define_insn "*fusion_p9_<mode>_constant" + [(set (match_operand:SDI 0 "int_reg_operand" "=r") + (unspec:SDI [(match_operand:SDI 1 "upper16_cint_operand" "L") + (match_operand:SDI 2 "u_short_cint_operand" "K")] + UNSPEC_FUSION_P9))] + "TARGET_P9_FUSION" +{ + emit_fusion_addis (operands[0], operands[1], "constant", "<MODE>"); + return "ori %0,%0,%2"; +} + [(set_attr "type" "two") + (set_attr "length" "8")]) + + +;; Optimize cases where we want to do a D-form load (register+offset) on +;; ISA 2.06/2.07 to an Altivec register, and the register allocator +;; has generated: +;; LFD 0,32(3) +;; XXLOR 32,0,0 +;; +;; and we change this to: +;; LI 0,32 +;; LXSDX 32,3,9 + +(define_peephole2 + [(match_scratch:DI 0 "b") + (set (match_operand:ALTIVEC_DFORM 1 "fpr_reg_operand") + (match_operand:ALTIVEC_DFORM 2 "simple_offsettable_mem_operand")) + (set (match_operand:ALTIVEC_DFORM 3 "altivec_register_operand") + (match_dup 1))] + "TARGET_VSX && TARGET_POWERPC64 && TARGET_UPPER_REGS_<MODE> + && !TARGET_P9_DFORM_SCALAR && peep2_reg_dead_p (2, operands[1])" + [(set (match_dup 0) + (match_dup 4)) + (set (match_dup 3) + (match_dup 5))] +{ + rtx tmp_reg = operands[0]; + rtx mem = operands[2]; + rtx addr = XEXP (mem, 0); + rtx add_op0, add_op1, new_addr; + + gcc_assert (GET_CODE (addr) == PLUS || GET_CODE (addr) == LO_SUM); + add_op0 = XEXP (addr, 0); + add_op1 = XEXP (addr, 1); + gcc_assert (REG_P (add_op0)); + new_addr = gen_rtx_PLUS (DImode, add_op0, tmp_reg); + + operands[4] = add_op1; + operands[5] = change_address (mem, <MODE>mode, new_addr); +}) + +;; Optimize cases were want to do a D-form store on ISA 2.06/2.07 from an +;; Altivec register, and the register allocator has generated: +;; XXLOR 0,32,32 +;; STFD 0,32(3) +;; +;; and we change this to: +;; LI 0,32 +;; STXSDX 32,3,9 + +(define_peephole2 + [(match_scratch:DI 0 "b") + (set (match_operand:ALTIVEC_DFORM 1 "fpr_reg_operand") + (match_operand:ALTIVEC_DFORM 2 "altivec_register_operand")) + (set (match_operand:ALTIVEC_DFORM 3 "simple_offsettable_mem_operand") + (match_dup 1))] + "TARGET_VSX && TARGET_POWERPC64 && TARGET_UPPER_REGS_<MODE> + && !TARGET_P9_DFORM_SCALAR && peep2_reg_dead_p (2, operands[1])" + [(set (match_dup 0) + (match_dup 4)) + (set (match_dup 5) + (match_dup 2))] +{ + rtx tmp_reg = operands[0]; + rtx mem = operands[3]; + rtx addr = XEXP (mem, 0); + rtx add_op0, add_op1, new_addr; + + gcc_assert (GET_CODE (addr) == PLUS || GET_CODE (addr) == LO_SUM); + add_op0 = XEXP (addr, 0); + add_op1 = XEXP (addr, 1); + gcc_assert (REG_P (add_op0)); + new_addr = gen_rtx_PLUS (DImode, add_op0, tmp_reg); + + operands[4] = add_op1; + operands[5] = change_address (mem, <MODE>mode, new_addr); +}) + + +;; Miscellaneous ISA 2.06 (power7) instructions +(define_insn "addg6s" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "register_operand" "r")] + UNSPEC_ADDG6S))] + "TARGET_POPCNTD" + "addg6s %0,%1,%2" + [(set_attr "type" "integer") + (set_attr "length" "4")]) + +(define_insn "cdtbcd" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "register_operand" "r")] + UNSPEC_CDTBCD))] + "TARGET_POPCNTD" + "cdtbcd %0,%1" + [(set_attr "type" "integer") + (set_attr "length" "4")]) + +(define_insn "cbcdtd" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "register_operand" "r")] + UNSPEC_CBCDTD))] + "TARGET_POPCNTD" + "cbcdtd %0,%1" + [(set_attr "type" "integer") + (set_attr "length" "4")]) + +(define_int_iterator UNSPEC_DIV_EXTEND [UNSPEC_DIVE + UNSPEC_DIVEO + UNSPEC_DIVEU + UNSPEC_DIVEUO]) + +(define_int_attr div_extend [(UNSPEC_DIVE "e") + (UNSPEC_DIVEO "eo") + (UNSPEC_DIVEU "eu") + (UNSPEC_DIVEUO "euo")]) + +(define_insn "div<div_extend>_<mode>" + [(set (match_operand:GPR 0 "register_operand" "=r") + (unspec:GPR [(match_operand:GPR 1 "register_operand" "r") + (match_operand:GPR 2 "register_operand" "r")] + UNSPEC_DIV_EXTEND))] + "TARGET_POPCNTD" + "div<wd><div_extend> %0,%1,%2" + [(set_attr "type" "div") + (set_attr "size" "<bits>")]) + + +;; Pack/unpack 128-bit floating point types that take 2 scalar registers + +; Type of the 64-bit part when packing/unpacking 128-bit floating point types +(define_mode_attr FP128_64 [(TF "DF") + (IF "DF") + (TD "DI") + (KF "DI")]) + +(define_expand "unpack<mode>" + [(set (match_operand:<FP128_64> 0 "nonimmediate_operand" "") + (unspec:<FP128_64> + [(match_operand:FMOVE128 1 "register_operand" "") + (match_operand:QI 2 "const_0_to_1_operand" "")] + UNSPEC_UNPACK_128BIT))] + "FLOAT128_2REG_P (<MODE>mode)" + "") + +(define_insn_and_split "unpack<mode>_dm" + [(set (match_operand:<FP128_64> 0 "nonimmediate_operand" "=d,m,d,r,m") + (unspec:<FP128_64> + [(match_operand:FMOVE128 1 "register_operand" "d,d,r,d,r") + (match_operand:QI 2 "const_0_to_1_operand" "i,i,i,i,i")] + UNSPEC_UNPACK_128BIT))] + "TARGET_POWERPC64 && TARGET_DIRECT_MOVE && FLOAT128_2REG_P (<MODE>mode)" + "#" + "&& reload_completed" + [(set (match_dup 0) (match_dup 3))] +{ + unsigned fp_regno = REGNO (operands[1]) + UINTVAL (operands[2]); + + if (REG_P (operands[0]) && REGNO (operands[0]) == fp_regno) + { + emit_note (NOTE_INSN_DELETED); + DONE; + } + + operands[3] = gen_rtx_REG (<FP128_64>mode, fp_regno); +} + [(set_attr "type" "fp,fpstore,mffgpr,mftgpr,store") + (set_attr "length" "4")]) + +(define_insn_and_split "unpack<mode>_nodm" + [(set (match_operand:<FP128_64> 0 "nonimmediate_operand" "=d,m") + (unspec:<FP128_64> + [(match_operand:FMOVE128 1 "register_operand" "d,d") + (match_operand:QI 2 "const_0_to_1_operand" "i,i")] + UNSPEC_UNPACK_128BIT))] + "(!TARGET_POWERPC64 || !TARGET_DIRECT_MOVE) && FLOAT128_2REG_P (<MODE>mode)" + "#" + "&& reload_completed" + [(set (match_dup 0) (match_dup 3))] +{ + unsigned fp_regno = REGNO (operands[1]) + UINTVAL (operands[2]); + + if (REG_P (operands[0]) && REGNO (operands[0]) == fp_regno) + { + emit_note (NOTE_INSN_DELETED); + DONE; + } + + operands[3] = gen_rtx_REG (<FP128_64>mode, fp_regno); +} + [(set_attr "type" "fp,fpstore") + (set_attr "length" "4")]) + +(define_insn_and_split "pack<mode>" + [(set (match_operand:FMOVE128 0 "register_operand" "=d,&d") + (unspec:FMOVE128 + [(match_operand:<FP128_64> 1 "register_operand" "0,d") + (match_operand:<FP128_64> 2 "register_operand" "d,d")] + UNSPEC_PACK_128BIT))] + "FLOAT128_2REG_P (<MODE>mode)" + "@ + fmr %L0,%2 + #" + "&& reload_completed && REGNO (operands[0]) != REGNO (operands[1])" + [(set (match_dup 3) (match_dup 1)) + (set (match_dup 4) (match_dup 2))] +{ + unsigned dest_hi = REGNO (operands[0]); + unsigned dest_lo = dest_hi + 1; + + gcc_assert (!IN_RANGE (REGNO (operands[1]), dest_hi, dest_lo)); + gcc_assert (!IN_RANGE (REGNO (operands[2]), dest_hi, dest_lo)); + + operands[3] = gen_rtx_REG (<FP128_64>mode, dest_hi); + operands[4] = gen_rtx_REG (<FP128_64>mode, dest_lo); +} + [(set_attr "type" "fpsimple,fp") + (set_attr "length" "4,8")]) + +(define_insn "unpack<mode>" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (unspec:DI [(match_operand:FMOVE128_VSX 1 "register_operand" "0,wa") + (match_operand:QI 2 "const_0_to_1_operand" "O,i")] + UNSPEC_UNPACK_128BIT))] + "VECTOR_MEM_ALTIVEC_OR_VSX_P (<MODE>mode)" +{ + if (REGNO (operands[0]) == REGNO (operands[1]) && INTVAL (operands[2]) == 0) + return ASM_COMMENT_START " xxpermdi to same register"; + + operands[3] = GEN_INT (INTVAL (operands[2]) == 0 ? 0 : 3); + return "xxpermdi %x0,%x1,%x1,%3"; +} + [(set_attr "type" "vecperm")]) + +(define_insn "pack<mode>" + [(set (match_operand:FMOVE128_VSX 0 "register_operand" "=wa") + (unspec:FMOVE128_VSX + [(match_operand:DI 1 "register_operand" "d") + (match_operand:DI 2 "register_operand" "d")] + UNSPEC_PACK_128BIT))] + "TARGET_VSX" + "xxpermdi %x0,%x1,%x2,0" + [(set_attr "type" "vecperm")]) + + + +;; ISA 2.08 IEEE 128-bit floating point support. + +(define_insn "add<mode>3" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (plus:IEEE128 + (match_operand:IEEE128 1 "altivec_register_operand" "v") + (match_operand:IEEE128 2 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xsaddqp %0,%1,%2" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn "sub<mode>3" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (minus:IEEE128 + (match_operand:IEEE128 1 "altivec_register_operand" "v") + (match_operand:IEEE128 2 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xssubqp %0,%1,%2" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn "mul<mode>3" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (mult:IEEE128 + (match_operand:IEEE128 1 "altivec_register_operand" "v") + (match_operand:IEEE128 2 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xsmulqp %0,%1,%2" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn "div<mode>3" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (div:IEEE128 + (match_operand:IEEE128 1 "altivec_register_operand" "v") + (match_operand:IEEE128 2 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xsdivqp %0,%1,%2" + [(set_attr "type" "vecdiv") + (set_attr "size" "128")]) + +(define_insn "sqrt<mode>2" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (sqrt:IEEE128 + (match_operand:IEEE128 1 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xssqrtqp %0,%1" + [(set_attr "type" "vecdiv") + (set_attr "size" "128")]) + +(define_expand "copysign<mode>3" + [(use (match_operand:IEEE128 0 "altivec_register_operand")) + (use (match_operand:IEEE128 1 "altivec_register_operand")) + (use (match_operand:IEEE128 2 "altivec_register_operand"))] + "FLOAT128_IEEE_P (<MODE>mode)" +{ + if (TARGET_FLOAT128_HW) + emit_insn (gen_copysign<mode>3_hard (operands[0], operands[1], + operands[2])); + else + { + rtx tmp = gen_reg_rtx (<MODE>mode); + emit_insn (gen_copysign<mode>3_soft (operands[0], operands[1], + operands[2], tmp)); + } + DONE; +}) + +(define_insn "copysign<mode>3_hard" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (unspec:IEEE128 + [(match_operand:IEEE128 1 "altivec_register_operand" "v") + (match_operand:IEEE128 2 "altivec_register_operand" "v")] + UNSPEC_COPYSIGN))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xscpsgnqp %0,%2,%1" + [(set_attr "type" "vecmove") + (set_attr "size" "128")]) + +(define_insn "copysign<mode>3_soft" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (unspec:IEEE128 + [(match_operand:IEEE128 1 "altivec_register_operand" "v") + (match_operand:IEEE128 2 "altivec_register_operand" "v") + (match_operand:IEEE128 3 "altivec_register_operand" "+v")] + UNSPEC_COPYSIGN))] + "!TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xscpsgndp %x3,%x2,%x1\;xxpermdi %x0,%x3,%x1,1" + [(set_attr "type" "veccomplex") + (set_attr "length" "8")]) + +(define_insn "neg<mode>2_hw" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (neg:IEEE128 + (match_operand:IEEE128 1 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xsnegqp %0,%1" + [(set_attr "type" "vecmove") + (set_attr "size" "128")]) + + +(define_insn "abs<mode>2_hw" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (abs:IEEE128 + (match_operand:IEEE128 1 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xsabsqp %0,%1" + [(set_attr "type" "vecmove") + (set_attr "size" "128")]) + + +(define_insn "*nabs<mode>2_hw" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (neg:IEEE128 + (abs:IEEE128 + (match_operand:IEEE128 1 "altivec_register_operand" "v"))))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xsnabsqp %0,%1" + [(set_attr "type" "vecmove") + (set_attr "size" "128")]) + +;; Initially don't worry about doing fusion +(define_insn "*fma<mode>4_hw" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (fma:IEEE128 + (match_operand:IEEE128 1 "altivec_register_operand" "%v") + (match_operand:IEEE128 2 "altivec_register_operand" "v") + (match_operand:IEEE128 3 "altivec_register_operand" "0")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xsmaddqp %0,%1,%2" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn "*fms<mode>4_hw" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (fma:IEEE128 + (match_operand:IEEE128 1 "altivec_register_operand" "%v") + (match_operand:IEEE128 2 "altivec_register_operand" "v") + (neg:IEEE128 + (match_operand:IEEE128 3 "altivec_register_operand" "0"))))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xsmsubqp %0,%1,%2" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn "*nfma<mode>4_hw" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (neg:IEEE128 + (fma:IEEE128 + (match_operand:IEEE128 1 "altivec_register_operand" "%v") + (match_operand:IEEE128 2 "altivec_register_operand" "v") + (match_operand:IEEE128 3 "altivec_register_operand" "0"))))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xsnmaddqp %0,%1,%2" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn "*nfms<mode>4_hw" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (neg:IEEE128 + (fma:IEEE128 + (match_operand:IEEE128 1 "altivec_register_operand" "%v") + (match_operand:IEEE128 2 "altivec_register_operand" "v") + (neg:IEEE128 + (match_operand:IEEE128 3 "altivec_register_operand" "0")))))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xsnmsubqp %0,%1,%2" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn "extend<SFDF:mode><IEEE128:mode>2_hw" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (float_extend:IEEE128 + (match_operand:SFDF 1 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<IEEE128:MODE>mode)" + "xscvdpqp %0,%1" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +;; Conversion between KFmode and TFmode if TFmode is ieee 128-bit floating +;; point is a simple copy. +(define_insn_and_split "extendkftf2" + [(set (match_operand:TF 0 "vsx_register_operand" "=wa,?wa") + (float_extend:TF (match_operand:KF 1 "vsx_register_operand" "0,wa")))] + "TARGET_FLOAT128_TYPE && TARGET_IEEEQUAD" + "@ + # + xxlor %x0,%x1,%x1" + "&& reload_completed && REGNO (operands[0]) == REGNO (operands[1])" + [(const_int 0)] +{ + emit_note (NOTE_INSN_DELETED); + DONE; +} + [(set_attr "type" "*,veclogical") + (set_attr "length" "0,4")]) + +(define_insn_and_split "trunctfkf2" + [(set (match_operand:KF 0 "vsx_register_operand" "=wa,?wa") + (float_extend:KF (match_operand:TF 1 "vsx_register_operand" "0,wa")))] + "TARGET_FLOAT128_TYPE && TARGET_IEEEQUAD" + "@ + # + xxlor %x0,%x1,%x1" + "&& reload_completed && REGNO (operands[0]) == REGNO (operands[1])" + [(const_int 0)] +{ + emit_note (NOTE_INSN_DELETED); + DONE; +} + [(set_attr "type" "*,veclogical") + (set_attr "length" "0,4")]) + +(define_insn "trunc<mode>df2_hw" + [(set (match_operand:DF 0 "altivec_register_operand" "=v") + (float_truncate:DF + (match_operand:IEEE128 1 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xscvqpdp %0,%1" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +;; There is no KFmode -> SFmode instruction. Preserve the accuracy by doing +;; the KFmode -> DFmode conversion using round to odd rather than the normal +;; conversion +(define_insn_and_split "trunc<mode>sf2_hw" + [(set (match_operand:SF 0 "vsx_register_operand" "=wy") + (float_truncate:SF + (match_operand:IEEE128 1 "altivec_register_operand" "v"))) + (clobber (match_scratch:DF 2 "=v"))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "#" + "&& 1" + [(set (match_dup 2) + (unspec:DF [(match_dup 1)] UNSPEC_ROUND_TO_ODD)) + (set (match_dup 0) + (float_truncate:SF (match_dup 2)))] +{ + if (GET_CODE (operands[2]) == SCRATCH) + operands[2] = gen_reg_rtx (DFmode); +} + [(set_attr "type" "vecfloat") + (set_attr "length" "8")]) + +;; Conversion between IEEE 128-bit and integer types +(define_insn "fix_<mode>di2_hw" + [(set (match_operand:DI 0 "altivec_register_operand" "=v") + (fix:DI (match_operand:IEEE128 1 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xscvqpsdz %0,%1" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn "fixuns_<mode>di2_hw" + [(set (match_operand:DI 0 "altivec_register_operand" "=v") + (unsigned_fix:DI (match_operand:IEEE128 1 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xscvqpudz %0,%1" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn "fix_<mode>si2_hw" + [(set (match_operand:SI 0 "altivec_register_operand" "=v") + (fix:SI (match_operand:IEEE128 1 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xscvqpswz %0,%1" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn "fixuns_<mode>si2_hw" + [(set (match_operand:SI 0 "altivec_register_operand" "=v") + (unsigned_fix:SI (match_operand:IEEE128 1 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xscvqpuwz %0,%1" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +;; Combiner pattern to prevent moving the result of converting an IEEE 128-bit +;; floating point value to 32-bit integer to GPR in order to save it. +(define_insn_and_split "*fix<uns>_<mode>_mem" + [(set (match_operand:SI 0 "memory_operand" "=Z") + (any_fix:SI (match_operand:IEEE128 1 "altivec_register_operand" "v"))) + (clobber (match_scratch:SI 2 "=v"))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "#" + "&& reload_completed" + [(set (match_dup 2) + (any_fix:SI (match_dup 1))) + (set (match_dup 0) + (match_dup 2))]) + +(define_insn "float_<mode>di2_hw" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (float:IEEE128 (match_operand:DI 1 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xscvsdqp %0,%1" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn_and_split "float_<mode>si2_hw" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (float:IEEE128 (match_operand:SI 1 "nonimmediate_operand" "vrZ"))) + (clobber (match_scratch:DI 2 "=v"))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "#" + "&& 1" + [(set (match_dup 2) + (sign_extend:DI (match_dup 1))) + (set (match_dup 0) + (float:IEEE128 (match_dup 2)))] +{ + if (GET_CODE (operands[2]) == SCRATCH) + operands[2] = gen_reg_rtx (DImode); +}) + +(define_insn_and_split "float<QHI:mode><IEEE128:mode>2" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v,v,v") + (float:IEEE128 (match_operand:QHI 1 "nonimmediate_operand" "v,r,Z"))) + (clobber (match_scratch:DI 2 "=X,r,X"))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<IEEE128:MODE>mode)" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx dest_di = gen_rtx_REG (DImode, REGNO (dest)); + + if (altivec_register_operand (src, <QHI:MODE>mode)) + emit_insn (gen_extend<QHI:mode>di2 (dest_di, src)); + else if (int_reg_operand (src, <QHI:MODE>mode)) + { + rtx ext_di = operands[2]; + emit_insn (gen_extend<QHI:mode>di2 (ext_di, src)); + emit_move_insn (dest_di, ext_di); + } + else if (MEM_P (src)) + { + rtx dest_qhi = gen_rtx_REG (<QHI:MODE>mode, REGNO (dest)); + emit_move_insn (dest_qhi, src); + emit_insn (gen_extend<QHI:mode>di2 (dest_di, dest_qhi)); + } + else + gcc_unreachable (); + + emit_insn (gen_float_<IEEE128:mode>di2_hw (dest, dest_di)); + DONE; +} + [(set_attr "length" "8,12,12") + (set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn "floatuns_<mode>di2_hw" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (unsigned_float:IEEE128 + (match_operand:DI 1 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xscvudqp %0,%1" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +(define_insn_and_split "floatuns_<mode>si2_hw" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v") + (unsigned_float:IEEE128 + (match_operand:SI 1 "nonimmediate_operand" "vrZ"))) + (clobber (match_scratch:DI 2 "=v"))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "#" + "&& 1" + [(set (match_dup 2) + (zero_extend:DI (match_dup 1))) + (set (match_dup 0) + (float:IEEE128 (match_dup 2)))] +{ + if (GET_CODE (operands[2]) == SCRATCH) + operands[2] = gen_reg_rtx (DImode); +}) + +(define_insn_and_split "floatuns<QHI:mode><IEEE128:mode>2" + [(set (match_operand:IEEE128 0 "altivec_register_operand" "=v,v,v") + (unsigned_float:IEEE128 + (match_operand:QHI 1 "nonimmediate_operand" "v,r,Z"))) + (clobber (match_scratch:DI 2 "=X,r,X"))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<IEEE128:MODE>mode)" + "#" + "&& reload_completed" + [(const_int 0)] +{ + rtx dest = operands[0]; + rtx src = operands[1]; + rtx dest_di = gen_rtx_REG (DImode, REGNO (dest)); + + if (altivec_register_operand (src, <QHI:MODE>mode) || MEM_P (src)) + emit_insn (gen_zero_extend<QHI:mode>di2 (dest_di, src)); + else if (int_reg_operand (src, <QHI:MODE>mode)) + { + rtx ext_di = operands[2]; + emit_insn (gen_zero_extend<QHI:mode>di2 (ext_di, src)); + emit_move_insn (dest_di, ext_di); + } + else + gcc_unreachable (); + + emit_insn (gen_floatuns_<IEEE128:mode>di2_hw (dest, dest_di)); + DONE; +} + [(set_attr "length" "8,12,8") + (set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +;; IEEE 128-bit instructions with round to odd semantics +(define_insn "*trunc<mode>df2_odd" + [(set (match_operand:DF 0 "vsx_register_operand" "=v") + (unspec:DF [(match_operand:IEEE128 1 "altivec_register_operand" "v")] + UNSPEC_ROUND_TO_ODD))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xscvqpdpo %0,%1" + [(set_attr "type" "vecfloat") + (set_attr "size" "128")]) + +;; IEEE 128-bit comparisons +(define_insn "*cmp<mode>_hw" + [(set (match_operand:CCFP 0 "cc_reg_operand" "=y") + (compare:CCFP (match_operand:IEEE128 1 "altivec_register_operand" "v") + (match_operand:IEEE128 2 "altivec_register_operand" "v")))] + "TARGET_FLOAT128_HW && FLOAT128_IEEE_P (<MODE>mode)" + "xscmpuqp %0,%1,%2" + [(set_attr "type" "veccmp") + (set_attr "size" "128")]) + + + +(include "sync.md") +(include "vector.md") +(include "vsx.md") +(include "altivec.md") +(include "spe.md") +(include "dfp.md") +(include "paired.md") +(include "crypto.md") +(include "htm.md") |