diff options
Diffstat (limited to 'gcc/config/s390')
-rw-r--r-- | gcc/config/s390/2064.md | 135 | ||||
-rw-r--r-- | gcc/config/s390/2084.md | 310 | ||||
-rw-r--r-- | gcc/config/s390/2097.md | 764 | ||||
-rw-r--r-- | gcc/config/s390/2817.md | 315 | ||||
-rw-r--r-- | gcc/config/s390/constraints.md | 492 | ||||
-rw-r--r-- | gcc/config/s390/linux-unwind.h | 130 | ||||
-rw-r--r-- | gcc/config/s390/linux.h | 104 | ||||
-rw-r--r-- | gcc/config/s390/predicates.md | 406 | ||||
-rw-r--r-- | gcc/config/s390/s390-modes.def | 174 | ||||
-rw-r--r-- | gcc/config/s390/s390-protos.h | 114 | ||||
-rw-r--r-- | gcc/config/s390/s390.c | 10845 | ||||
-rw-r--r-- | gcc/config/s390/s390.h | 954 | ||||
-rw-r--r-- | gcc/config/s390/s390.md | 9410 | ||||
-rw-r--r-- | gcc/config/s390/s390.opt | 99 | ||||
-rw-r--r-- | gcc/config/s390/s390x.h | 27 | ||||
-rw-r--r-- | gcc/config/s390/t-linux64 | 11 | ||||
-rw-r--r-- | gcc/config/s390/tpf-unwind.h | 252 | ||||
-rw-r--r-- | gcc/config/s390/tpf.h | 130 | ||||
-rw-r--r-- | gcc/config/s390/tpf.md | 33 | ||||
-rw-r--r-- | gcc/config/s390/tpf.opt | 27 |
20 files changed, 24732 insertions, 0 deletions
diff --git a/gcc/config/s390/2064.md b/gcc/config/s390/2064.md new file mode 100644 index 000000000..143978334 --- /dev/null +++ b/gcc/config/s390/2064.md @@ -0,0 +1,135 @@ +;; Scheduling description for z900 (cpu 2064). +;; Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation, Inc. +;; Contributed by Hartmut Penner (hpenner@de.ibm.com) and +;; Ulrich Weigand (uweigand@de.ibm.com). + +;; 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/>. + +;; +;; References: +;; The microarchitecture of the IBM eServer z900 processor. +;; E.M. Schwarz et al. +;; IBM Journal of Research and Development Vol. 46 No 4/5, 2002. +;; +;; z900 (cpu 2064) pipeline +;; +;; dec +;; --> | <--- +;; LA bypass | agen | +;; | | | +;; --- c1 | Load bypass +;; | | +;; c2---- +;; | +;; e1 +;; | +;; wr + +;; This scheduler description is also used for the g5 and g6. + +(define_automaton "z_ipu") +(define_cpu_unit "z_e1" "z_ipu") +(define_cpu_unit "z_wr" "z_ipu") + + +(define_insn_reservation "z_la" 1 + (and (eq_attr "cpu" "z900,g5,g6") + (eq_attr "type" "la")) + "z_e1,z_wr") + +(define_insn_reservation "z_larl" 1 + (and (eq_attr "cpu" "z900,g5,g6") + (eq_attr "type" "larl")) + "z_e1,z_wr") + +(define_insn_reservation "z_load" 1 + (and (eq_attr "cpu" "z900,g5,g6") + (eq_attr "type" "load")) + "z_e1,z_wr") + +(define_insn_reservation "z_store" 1 + (and (eq_attr "cpu" "z900,g5,g6") + (eq_attr "type" "store")) + "z_e1,z_wr") + +(define_insn_reservation "z_sem" 2 + (and (eq_attr "cpu" "z900,g5,g6") + (eq_attr "type" "sem")) + "z_e1*2,z_wr") + +(define_insn_reservation "z_call" 5 + (and (eq_attr "cpu" "z900,g5,g6") + (eq_attr "type" "jsr")) + "z_e1*5,z_wr") + +(define_insn_reservation "z_mul" 5 + (and (eq_attr "cpu" "g5,g6,z900") + (eq_attr "type" "imulsi,imulhi")) + "z_e1*5,z_wr") + +(define_insn_reservation "z_inf" 10 + (and (eq_attr "cpu" "g5,g6,z900") + (eq_attr "type" "idiv,imuldi")) + "z_e1*10,z_wr") + +;; For everything else we check the atype flag. + +(define_insn_reservation "z_int" 1 + (and (eq_attr "cpu" "z900,g5,g6") + (and (not (eq_attr "type" "la,larl,load,store,jsr")) + (eq_attr "atype" "reg"))) + "z_e1,z_wr") + +(define_insn_reservation "z_agen" 1 + (and (eq_attr "cpu" "z900,g5,g6") + (and (not (eq_attr "type" "la,larl,load,store,jsr")) + (eq_attr "atype" "agen"))) + "z_e1,z_wr") + +;; +;; s390_agen_dep_p returns 1, if a register is set in the +;; first insn and used in the dependent insn to form a address. +;; + +;; +;; If an instruction uses a register to address memory, it needs +;; to be set 5 cycles in advance. +;; + +(define_bypass 5 "z_int,z_agen" + "z_agen,z_la,z_call,z_load,z_store" "s390_agen_dep_p") + +;; +;; A load type instruction uses a bypass to feed the result back +;; to the address generation pipeline stage. +;; + +(define_bypass 3 "z_load" + "z_agen,z_la,z_call,z_load,z_store" "s390_agen_dep_p") + +;; +;; A load address type instruction uses a bypass to feed the +;; result back to the address generation pipeline stage. +;; + +(define_bypass 2 "z_larl,z_la" + "z_agen,z_la,z_call,z_load,z_store" "s390_agen_dep_p") + + + + + diff --git a/gcc/config/s390/2084.md b/gcc/config/s390/2084.md new file mode 100644 index 000000000..9ce5530b6 --- /dev/null +++ b/gcc/config/s390/2084.md @@ -0,0 +1,310 @@ +;; Scheduling description for z990 (cpu 2084). +;; Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2010 +;; Free Software Foundation, Inc. +;; Contributed by Hartmut Penner (hpenner@de.ibm.com) and +;; Ulrich Weigand (uweigand@de.ibm.com). + +;; 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/>. + +(define_automaton "x_ipu") + +(define_cpu_unit "x_e1_r,x_e1_s,x_e1_t" "x_ipu") +(define_cpu_unit "x_wr_r,x_wr_s,x_wr_t,x_wr_fp" "x_ipu") +(define_cpu_unit "x_s1,x_s2,x_s3,x_s4" "x_ipu") +(define_cpu_unit "x_t1,x_t2,x_t3,x_t4" "x_ipu") +(define_cpu_unit "x_f1,x_f2,x_f3,x_f4,x_f5,x_f6" "x_ipu") +(define_cpu_unit "x_store_tok" "x_ipu") +(define_cpu_unit "x_ms,x_mt" "x_ipu") + +(define_reservation "x-e1-st" "(x_e1_s | x_e1_t)") + +(define_reservation "x-e1-np" "(x_e1_r + x_e1_s + x_e1_t)") + +(absence_set "x_e1_r" "x_e1_s,x_e1_t") +(absence_set "x_e1_s" "x_e1_t") + +;; Try to avoid int <-> fp transitions. + +(define_reservation "x-x" "x_s1|x_t1,x_s2|x_t2,x_s3|x_t3,x_s4|x_t4") +(define_reservation "x-f" "x_f1,x_f2,x_f3,x_f4,x_f5,x_f6") +(define_reservation "x-wr-st" "((x_wr_s | x_wr_t),x-x)") +(define_reservation "x-wr-np" "((x_wr_r + x_wr_s + x_wr_t),x-x)") +(define_reservation "x-wr-fp" "x_wr_fp,x-f") +(define_reservation "x-mem" "x_ms|x_mt") + +(absence_set "x_wr_fp" + "x_s1,x_s2,x_s3,x_s4,x_t1,x_t2,x_t3,x_t4,x_wr_s,x_wr_t") + +(absence_set "x_e1_r,x_wr_r,x_wr_s,x_wr_t" + "x_f1,x_f2,x_f3,x_f4,x_f5,x_f6,x_wr_fp") + +;; Don't have any load type insn in same group as store + +(absence_set "x_ms,x_mt" "x_store_tok") + + +;; +;; Simple insns +;; + +(define_insn_reservation "x_int" 1 + (and (eq_attr "cpu" "z990,z9_109") + (and (eq_attr "type" "integer") + (eq_attr "atype" "reg"))) + "x-e1-st,x-wr-st") + +(define_insn_reservation "x_agen" 1 + (and (eq_attr "cpu" "z990,z9_109") + (and (eq_attr "type" "integer") + (eq_attr "atype" "agen"))) + "x-e1-st,x-wr-st") + +(define_insn_reservation "x_lr" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "lr")) + "x-e1-st,x-wr-st") + +(define_insn_reservation "x_la" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "la")) + "x-e1-st,x-wr-st") + +(define_insn_reservation "x_larl" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "larl")) + "x-e1-st,x-wr-st") + +(define_insn_reservation "x_load" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "load")) + "x-e1-st+x-mem,x-wr-st") + +(define_insn_reservation "x_store" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "store")) + "x-e1-st+x_store_tok,x-wr-st") + +(define_insn_reservation "x_branch" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "branch")) + "x_e1_r,x_wr_r") + +(define_insn_reservation "x_call" 5 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "jsr")) + "x-e1-np*5,x-wr-np") + +(define_insn_reservation "x_mul_hi" 2 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "imulhi")) + "x-e1-np*2,x-wr-np") + +(define_insn_reservation "x_mul_sidi" 4 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "imulsi,imuldi")) + "x-e1-np*4,x-wr-np") + +(define_insn_reservation "x_div" 10 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "idiv")) + "x-e1-np*10,x-wr-np") + +(define_insn_reservation "x_sem" 17 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "sem")) + "x-e1-np+x-mem,x-e1-np*16,x-wr-st") + +;; +;; Multicycle insns +;; + +(define_insn_reservation "x_cs" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "cs")) + "x-e1-np,x-wr-np") + +(define_insn_reservation "x_vs" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "vs")) + "x-e1-np*10,x-wr-np") + +(define_insn_reservation "x_stm" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "stm")) + "(x-e1-np+x_store_tok)*10,x-wr-np") + +(define_insn_reservation "x_lm" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "lm")) + "x-e1-np*10,x-wr-np") + +(define_insn_reservation "x_other" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "other")) + "x-e1-np,x-wr-np") + +;; +;; Floating point insns +;; + +(define_insn_reservation "x_fsimptf" 7 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "fsimptf,fhex")) + "x_e1_t*2,x-wr-fp") + +(define_insn_reservation "x_fsimpdf" 6 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "fsimpdf,fmuldf,fmadddf,fhex")) + "x_e1_t,x-wr-fp") + +(define_insn_reservation "x_fsimpsf" 6 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "fsimpsf,fmulsf,fmaddsf,fhex")) + "x_e1_t,x-wr-fp") + + +(define_insn_reservation "x_fmultf" 33 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "fmultf")) + "x_e1_t*27,x-wr-fp") + + +(define_insn_reservation "x_fdivtf" 82 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "fdivtf,fsqrttf")) + "x_e1_t*76,x-wr-fp") + +(define_insn_reservation "x_fdivdf" 36 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "fdivdf,fsqrtdf")) + "x_e1_t*30,x-wr-fp") + +(define_insn_reservation "x_fdivsf" 36 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "fdivsf,fsqrtsf")) + "x_e1_t*30,x-wr-fp") + + +(define_insn_reservation "x_floadtf" 6 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "floadtf")) + "x_e1_t,x-wr-fp") + +(define_insn_reservation "x_floaddf" 6 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "floaddf")) + "x_e1_t,x-wr-fp") + +(define_insn_reservation "x_floadsf" 6 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "floadsf")) + "x_e1_t,x-wr-fp") + + +(define_insn_reservation "x_fstoredf" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "fstoredf")) + "x_e1_t,x-wr-fp") + +(define_insn_reservation "x_fstoresf" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "fstoresf")) + "x_e1_t,x-wr-fp") + + +(define_insn_reservation "x_ftrunctf" 16 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "ftrunctf")) + "x_e1_t*10,x-wr-fp") + +(define_insn_reservation "x_ftruncdf" 11 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "ftruncdf")) + "x_e1_t*5,x-wr-fp") + + +(define_insn_reservation "x_ftoi" 1 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "ftoi")) + "x_e1_t*3,x-wr-fp") + +(define_insn_reservation "x_itof" 7 + (and (eq_attr "cpu" "z990,z9_109") + (eq_attr "type" "itoftf,itofdf,itofsf")) + "x_e1_t*3,x-wr-fp") + +(define_bypass 1 "x_fsimpdf" "x_fstoredf") + +(define_bypass 1 "x_fsimpsf" "x_fstoresf") + +(define_bypass 1 "x_floaddf" "x_fsimpdf,x_fstoredf,x_floaddf") + +(define_bypass 1 "x_floadsf" "x_fsimpsf,x_fstoresf,x_floadsf") + +;; +;; s390_agen_dep_p returns 1, if a register is set in the +;; first insn and used in the dependent insn to form a address. +;; + +;; +;; If an instruction uses a register to address memory, it needs +;; to be set 5 cycles in advance. +;; + +(define_bypass 5 "x_int,x_agen,x_lr" + "x_agen,x_la,x_branch,x_call,x_load,x_store,x_cs,x_stm,x_lm,x_other" + "s390_agen_dep_p") + +(define_bypass 9 "x_int,x_agen,x_lr" + "x_floadtf, x_floaddf, x_floadsf, x_fstoredf, x_fstoresf,\ + x_fsimpdf, x_fsimpsf, x_fdivdf, x_fdivsf" + "s390_agen_dep_p") +;; +;; A load type instruction uses a bypass to feed the result back +;; to the address generation pipeline stage. +;; + +(define_bypass 4 "x_load" + "x_agen,x_la,x_branch,x_call,x_load,x_store,x_cs,x_stm,x_lm,x_other" + "s390_agen_dep_p") + +(define_bypass 5 "x_load" + "x_floadtf, x_floaddf, x_floadsf, x_fstoredf, x_fstoresf,\ + x_fsimpdf, x_fsimpsf, x_fdivdf, x_fdivsf" + "s390_agen_dep_p") + +;; +;; A load address type instruction uses a bypass to feed the +;; result back to the address generation pipeline stage. +;; + +(define_bypass 3 "x_larl,x_la" + "x_agen,x_la,x_branch,x_call,x_load,x_store,x_cs,x_stm,x_lm,x_other" + "s390_agen_dep_p") + +(define_bypass 5 "x_larl, x_la" + "x_floadtf, x_floaddf, x_floadsf, x_fstoredf, x_fstoresf,\ + x_fsimpdf, x_fsimpsf, x_fdivdf, x_fdivsf" + "s390_agen_dep_p") + +;; +;; Operand forwarding +;; + +(define_bypass 0 "x_lr,x_la,x_load" "x_int,x_lr") + + diff --git a/gcc/config/s390/2097.md b/gcc/config/s390/2097.md new file mode 100644 index 000000000..77c206ecd --- /dev/null +++ b/gcc/config/s390/2097.md @@ -0,0 +1,764 @@ +;; Scheduling description for z10 (cpu 2097). +;; Copyright (C) 2008, 2010 Free Software Foundation, Inc. +;; Contributed by Wolfgang Gellerich (gellerich@de.ibm.com). + + +; General naming conventions used in this file: +; - The two pipelines are called S and T, respectively. +; - A name ending "_S" or "_T" indicates that something happens in +; (or belongs to) this pipeline. +; - A name ending "_ANY" indicates that something happens in (or belongs +; to) either of the two pipelines. +; - A name ending "_BOTH" indicates that something happens in (or belongs +; to) both pipelines. + + +;; Automaton and components. + +(define_automaton "z10_cpu") + +(define_cpu_unit "z10_e1_S, z10_e1_T" "z10_cpu") +(define_reservation "z10_e1_ANY" "(z10_e1_S | z10_e1_T)") +(define_reservation "z10_e1_BOTH" "(z10_e1_S + z10_e1_T)") + + +; Both pipelines can execute a branch instruction, and branch +; instructions can be grouped with all other groupable instructions +; but not with a second branch instruction. + +(define_cpu_unit "z10_branch_ANY" "z10_cpu") + +(define_insn_reservation "z10_branch" 4 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "branch")) + "z10_branch_ANY + z10_e1_ANY, z10_Gate_ANY") + + +; Z10 operand and result forwarding. + +; Instructions marked with the attributes as z10_fwd or z10_fr can +; forward a value they load from one of their operants into a register +; if the instruction in the second pipeline reads the same register. +; The second operation must be superscalar. Instructions marked as +; z10_rec or z10_fr can receive a value they read from a register is +; this register gets updated by an instruction in the first pipeline. +; The first instruction must be superscalar. + + +; Forwarding from z10_fwd and z10_fr to z10_super. + +(define_bypass 0 "z10_la_fwd, z10_la_fwd_A1, z10_larl_fwd, z10_larl_fwd_A3, \ + z10_load_fwd, z10_load_fwd_A3, \ + z10_other_fwd, z10_other_fwd_A1, z10_other_fwd_A3, \ + z10_other_fr, z10_other_fr_A3, z10_other_fr_E1, \ + z10_other_fwd_E1, z10_lr_fr, z10_lr_fr_E1, \ + z10_int_fwd, z10_int_fwd_A1, z10_int_fwd_A3, \ + z10_int_fwd_E1, z10_int_fr, z10_int_fr_E1, \ + z10_int_fr_A3" + "z10_other_super, z10_other_super_c_E1, z10_other_super_E1, \ + z10_int_super, z10_int_super_E1, \ + z10_lr, z10_store_super" + " ! s390_agen_dep_p") + + +; Forwarding from z10_super to frz10_ and z10_rec. + +(define_bypass 0 "z10_other_super, z10_other_super_E1, z10_other_super_c_E1, \ + z10_int_super, z10_int_super_E1, \ + z10_larl_super_E1, z10_larl_super, \ + z10_store_super" + "z10_int_fr, z10_int_fr_E1, z10_int_fr_A3, \ + z10_other_fr, z10_other_fr_A3, z10_lr_fr, z10_lr_fr_E1, \ + z10_other_fr_E1, z10_store_rec" + " ! s390_agen_dep_p") + + +; Forwarding from z10_fwd and z10_fr to z10_rec and z10_fr. + +(define_bypass 0 "z10_la_fwd, z10_la_fwd_A1, z10_larl_fwd, z10_larl_fwd_A3, \ + z10_load_fwd, z10_load_fwd_A3, \ + z10_other_fwd, z10_other_fwd_A1, z10_other_fwd_A3, \ + z10_other_fr, z10_other_fr_A3, z10_other_fr_E1, \ + z10_other_fwd_E1, \ + z10_lr_fr, z10_lr_fr_E1, \ + z10_int_fwd, z10_int_fwd_A1, z10_int_fwd_A3, \ + z10_int_fwd_E1, z10_int_fr, z10_int_fr_E1, \ + z10_int_fr_A3" + "z10_int_fr, z10_int_fr_E1, z10_int_fr_A3, \ + z10_other_fr, z10_other_fr_A3, z10_lr_fr, z10_lr_fr_E1, \ + z10_other_fr_E1, z10_store_rec" + " ! s390_agen_dep_p") + + +; +; Simple insns +; + +; Here is the cycle diagram for FXU-executed instructions: +; ... A1 A2 A3 E1 P1 P2 P3 R0 ... +; ^ ^ ^ +; | | updated GPR is available +; | write to GPR +; instruction reads GPR during this cycle + + +; Variants of z10_int follow. + +(define_insn_reservation "z10_int" 6 + (and (and (eq_attr "cpu" "z10") + (eq_attr "type" "integer")) + (and (eq_attr "atype" "reg") + (and (and (eq_attr "z10prop" "!z10_super") + (eq_attr "z10prop" "!z10_super_c")) + (and (and (and (and (eq_attr "z10prop" "!z10_super_E1") + (eq_attr "z10prop" "!z10_super_c_E1")) + (eq_attr "z10prop" "!z10_fwd")) + (and (eq_attr "z10prop" "!z10_fwd_A1") + (eq_attr "z10prop" "!z10_fwd_A3"))) + (and (and (eq_attr "z10prop" "!z10_fwd_E1") + (eq_attr "z10prop" "!z10_fr")) + (and (eq_attr "z10prop" "!z10_fr_E1") + (eq_attr "z10prop" "!z10_fr_A3"))))))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_int_super" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "integer") + (and (eq_attr "atype" "reg") + (ior (eq_attr "z10prop" "z10_super") + (eq_attr "z10prop" "z10_super_c"))))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_int_super_E1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "integer") + (and (eq_attr "atype" "reg") + (ior (eq_attr "z10prop" "z10_super_E1") + (eq_attr "z10prop" "z10_super_c_E1"))))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_int_fwd" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "integer") + (and (eq_attr "atype" "reg") + (eq_attr "z10prop" "z10_fwd")))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_int_fwd_A1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "integer") + (and (eq_attr "atype" "reg") + (eq_attr "z10prop" "z10_fwd_A1")))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_int_fwd_A3" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "integer") + (and (eq_attr "atype" "reg") + (eq_attr "z10prop" "z10_fwd_A3")))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_int_fwd_E1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "integer") + (and (eq_attr "atype" "reg") + (eq_attr "z10prop" "z10_fwd_E1")))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_int_fr" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "integer") + (and (eq_attr "atype" "reg") + (eq_attr "z10prop" "z10_fr")))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_int_fr_E1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "integer") + (and (eq_attr "atype" "reg") + (eq_attr "z10prop" "z10_fr_E1")))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_int_fr_A3" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "integer") + (and (eq_attr "atype" "reg") + (eq_attr "z10prop" "z10_fr_A3")))) + "z10_e1_ANY, z10_Gate_ANY") + +; END of z10_int variants + + +(define_insn_reservation "z10_agen" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "integer") + (eq_attr "atype" "agen"))) + "z10_e1_ANY, z10_Gate_ANY") + + +(define_insn_reservation "z10_lr" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "lr") + (and (eq_attr "z10prop" "!z10_fr") + (eq_attr "z10prop" "!z10_fr_E1")))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_lr_fr" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "lr") + (eq_attr "z10prop" "z10_fr"))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_lr_fr_E1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "lr") + (eq_attr "z10prop" "z10_fr_E1"))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_la" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "la") + (and (eq_attr "z10prop" "!z10_fwd") + (eq_attr "z10prop" "!z10_fwd_A1")))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_la_fwd" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "la") + (eq_attr "z10prop" "z10_fwd"))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_la_fwd_A1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "la") + (eq_attr "z10prop" "z10_fwd_A1"))) + "z10_e1_ANY, z10_Gate_ANY") + + +; larl-type instructions + +(define_insn_reservation "z10_larl" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "larl") + (and (eq_attr "z10prop" "!z10_super_A1") + (and (eq_attr "z10prop" "!z10_fwd") + (and (eq_attr "z10prop" "!z10_fwd_A3") + (and (eq_attr "z10prop" "!z10_super") + (eq_attr "z10prop" "!z10_super_c")) + (and (eq_attr "z10prop" "!z10_super_E1") + (eq_attr "z10prop" "!z10_super_c_E1"))))))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_larl_super" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "larl") + (and (eq_attr "z10prop" "z10_super") + (eq_attr "z10prop" "z10_super_c")))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_larl_fwd" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "larl") + (eq_attr "z10prop" "z10_fwd"))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_larl_fwd_A3" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "larl") + (eq_attr "z10prop" "z10_fwd_A3"))) + "z10_e1_ANY, z10_Gate_ANY") + + +(define_insn_reservation "z10_larl_A1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "larl") + (eq_attr "z10prop" "z10_super_A1"))) + "z10_e1_ANY, z10_Gate_ANY") +; "z10_e1_ANY") + +(define_insn_reservation "z10_larl_super_E1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "larl") + (ior (eq_attr "z10prop" "z10_super_E1") + (eq_attr "z10prop" "z10_super_c_E1")))) + "z10_e1_ANY, z10_Gate_ANY") +; "z10_e1_ANY") + + +(define_insn_reservation "z10_load" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "load") + (and (eq_attr "z10prop" "!z10_fwd") + (eq_attr "z10prop" "!z10_fwd_A3")))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_load_fwd" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "load") + (eq_attr "z10prop" "z10_fwd"))) + "z10_e1_ANY, z10_Gate_ANY") +; "z10_e1_ANY") + +(define_insn_reservation "z10_load_fwd_A3" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "load") + (eq_attr "z10prop" "z10_fwd_A3"))) + "z10_e1_ANY, z10_Gate_ANY") +; "z10_e1_ANY") + +(define_insn_reservation "z10_store" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "store") + (and (eq_attr "z10prop" "!z10_rec") + (and (eq_attr "z10prop" "!z10_super") + (eq_attr "z10prop" "!z10_super_c"))))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_store_super" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "store") + (ior (eq_attr "z10prop" "z10_super") + (eq_attr "z10prop" "z10_super_c")))) + "z10_e1_ANY, z10_Gate_ANY") + +(define_insn_reservation "z10_store_rec" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "store") + (eq_attr "z10prop" "z10_rec"))) + "z10_e1_ANY, z10_Gate_ANY") + +; The default_latency is chosen to drain off the pipeline. +(define_insn_reservation "z10_call" 14 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "jsr")) + "z10_e1_BOTH*4, z10_Gate_BOTH") + +; The default latency is for worst case. CS and CSG take one +; cycle only (i.e. latency would be 6). +(define_insn_reservation "z10_sem" 9 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "sem")) + "z10_e1_BOTH*5, z10_Gate_ANY") + +(define_insn_reservation "z10_cs" 6 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "cs")) + "z10_e1_BOTH, z10_Gate_BOTH") + +(define_insn_reservation "z10_vs" 6 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "vs")) + "z10_e1_BOTH*4, z10_Gate_BOTH") + +; Load and store multiple. Actual number of cycles +; in unknown at compile.time. +(define_insn_reservation "z10_stm" 10 + (and (eq_attr "cpu" "z10") + (ior (eq_attr "type" "stm") + (eq_attr "type" "lm"))) + "z10_e1_BOTH*4, z10_Gate_BOTH") + + +; Subsets of z10_other follow. + +(define_insn_reservation "z10_other" 6 + (and (and (eq_attr "cpu" "z10") + (eq_attr "type" "other")) + (and (and (eq_attr "z10prop" "!z10_fwd") + (eq_attr "z10prop" "!z10_fwd_A1")) + (and (and (and (eq_attr "z10prop" "!z10_fr_A3") + (eq_attr "z10prop" "!z10_fwd_A3")) + (and (eq_attr "z10prop" "!z10_fr") + (eq_attr "z10prop" "!z10_fr_E1"))) + (and (and (and (eq_attr "z10prop" "!z10_super") + (eq_attr "z10prop" "!z10_super_c")) + (eq_attr "z10prop" "!z10_super_c_E1")) + (and (eq_attr "z10prop" "!z10_super_E1") + (eq_attr "z10prop" "!z10_fwd_E1")))))) + "z10_e1_BOTH, z10_Gate_BOTH") + +(define_insn_reservation "z10_other_fr_E1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "other") + (eq_attr "z10prop" "z10_fr_E1"))) + "z10_e1_BOTH, z10_Gate_BOTH") + +(define_insn_reservation "z10_other_super_c_E1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "other") + (eq_attr "z10prop" "z10_super_c_E1"))) + "z10_e1_BOTH, z10_Gate_BOTH") + +(define_insn_reservation "z10_other_super_E1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "other") + (eq_attr "z10prop" "z10_super_E1"))) + "z10_e1_BOTH, z10_Gate_BOTH") + +(define_insn_reservation "z10_other_fwd_E1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "other") + (eq_attr "z10prop" "z10_fwd_E1"))) + "z10_e1_BOTH, z10_Gate_BOTH") + +(define_insn_reservation "z10_other_fwd" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "other") + (eq_attr "z10prop" "z10_fwd"))) + "z10_e1_BOTH, z10_Gate_BOTH") + +(define_insn_reservation "z10_other_fwd_A3" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "other") + (eq_attr "z10prop" "z10_fwd_A3"))) + "z10_e1_BOTH, z10_Gate_BOTH") + +(define_insn_reservation "z10_other_fwd_A1" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "other") + (eq_attr "z10prop" "z10_fwd_A1"))) + "z10_e1_BOTH, z10_Gate_BOTH") + +(define_insn_reservation "z10_other_fr" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "other") + (eq_attr "z10prop" "z10_fr"))) + "z10_e1_BOTH, z10_Gate_BOTH") + +(define_insn_reservation "z10_other_fr_A3" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "other") + (eq_attr "z10prop" "z10_fr_A3"))) + "z10_e1_BOTH, z10_Gate_BOTH") + +(define_insn_reservation "z10_other_super" 6 + (and (eq_attr "cpu" "z10") + (and (eq_attr "type" "other") + (ior (eq_attr "z10prop" "z10_super") + (eq_attr "z10prop" "z10_super_c")))) + "z10_e1_BOTH, z10_Gate_BOTH") + +; END of z10_other subsets. + + +; +; Floating point insns +; + +; Z10 executes the following integer operations in the BFU pipeline. + +(define_insn_reservation "z10_mul_sidi" 12 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "imulsi,imuldi,imulhi")) + "z10_e1_BOTH, z10_Gate_FP") + +; Some variants take fewer cycles, but that is not relevant here. +(define_insn_reservation "z10_div" 162 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "idiv")) + "z10_e1_BOTH*4, z10_Gate_FP") + + +; BFP multiplication and general instructions + +(define_insn_reservation "z10_fsimpdf" 6 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fsimpdf,fmuldf,fmadddf")) + "z10_e1_BOTH, z10_Gate_FP") + +(define_insn_reservation "z10_fsimpsf" 6 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fsimpsf,fmulsf,fmaddsf")) + "z10_e1_BOTH, z10_Gate_FP") + +(define_insn_reservation "z10_fmultf" 52 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fmultf")) + "z10_e1_BOTH*4, z10_Gate_FP") + +(define_insn_reservation "z10_fsimptf" 14 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fsimptf")) + "z10_e1_BOTH*2, z10_Gate_FP") + + +; BFP division + +(define_insn_reservation "z10_fdivtf" 113 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fdivtf")) + "z10_e1_T*4, z10_Gate_FP") + +(define_insn_reservation "z10_fdivdf" 41 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fdivdf")) + "z10_e1_T*4, z10_Gate_FP") + +(define_insn_reservation "z10_fdivsf" 34 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fdivsf")) + "z10_e1_T*4, z10_Gate_FP") + + +; BFP sqrt + +(define_insn_reservation "z10_fsqrtsf" 41 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fsqrtsf")) + "z10_e1_T*4, z10_Gate_FP") + +(define_insn_reservation "z10_fsqrtdf" 54 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fsqrtdf")) + "z10_e1_T*4, z10_Gate_FP") + +(define_insn_reservation "z10_fsqrtf" 122 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fsqrttf")) + "z10_e1_T*4, z10_Gate_FP") + + +; BFP load and store + +(define_insn_reservation "z10_floadtf" 12 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "floadtf")) + "z10_e1_T, z10_Gate_FP") + +(define_insn_reservation "z10_floaddf" 1 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "floaddf")) + "z10_e1_T, z10_Gate_FP") + +(define_insn_reservation "z10_floadsf" 1 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "floadsf")) + "z10_e1_T, z10_Gate_FP") + +(define_insn_reservation "z10_fstoredf" 12 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fstoredf,fstoredd")) + "z10_e1_T, z10_Gate_FP") + +(define_insn_reservation "z10_fstoresf" 12 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fstoresf,fstoresd")) + "z10_e1_T, z10_Gate_FP") + + +; BFP truncate +(define_insn_reservation "z10_ftrunctf" 16 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "ftrunctf")) + "z10_e1_T, z10_Gate_FP") + +(define_insn_reservation "z10_ftruncdf" 12 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "ftruncdf")) + "z10_e1_T, z10_Gate_FP") + + +; Conversion between BFP and int. +(define_insn_reservation "z10_ftoi" 13 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "ftoi")) + "z10_e1_T, z10_Gate_FP") + +(define_insn_reservation "z10_itoftf" 14 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "itoftf")) + "z10_e1_T*2, z10_Gate_FP") + +(define_insn_reservation "z10_itofsfdf" 12 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "itofdf,itofsf")) + "z10_e1_T, z10_Gate_FP") + + + +; BFP-related bypasses. There is no bypass for extended mode. +(define_bypass 1 "z10_fsimpdf" "z10_fstoredf") +(define_bypass 1 "z10_fsimpsf" "z10_fstoresf") +(define_bypass 1 "z10_floaddf" "z10_fsimpdf, z10_fstoredf") +(define_bypass 1 "z10_floadsf" "z10_fsimpsf, z10_fstoresf") + + +; +; insn_reservations for DFP instructions. +; + +; Exact number of cycles is not known at compile-time. +(define_insn_reservation "z10_fdivddtd" 40 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fdivdd,fdivtd")) + "z10_e1_BOTH,z10_Gate_DFU") + +(define_insn_reservation "z10_ftruncsd" 38 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "ftruncsd")) + "z10_e1_BOTH*4,z10_Gate_DFU") + +(define_insn_reservation "z10_ftruncdd" 340 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "ftruncsd")) + "z10_e1_BOTH*4,z10_Gate_DFU") + +(define_insn_reservation "z10_floaddd" 12 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "floaddd")) + "z10_e1_BOTH,z10_Gate_DFU") + +(define_insn_reservation "z10_floadsd" 12 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "floadsd")) + "z10_e1_BOTH,z10_Gate_DFU") + +; Exact number of cycles is not known at compile-time. +(define_insn_reservation "z10_fmulddtd" 35 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fmuldd,fmultd")) + "z10_e1_BOTH,z10_Gate_DFU") + +(define_insn_reservation "z10_fsimpdd" 17 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fsimpdd")) + "z10_e1_BOTH,z10_Gate_DFU") + +(define_insn_reservation "z10_fsimpsd" 17 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fsimpsd")) + "z10_e1_BOTH,z10_Gate_DFU") + +(define_insn_reservation "z10_fsimptd" 18 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "fsimptd")) + "z10_e1_BOTH,z10_Gate_DFU") + +(define_insn_reservation "z10_itofdd" 36 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "itofdd")) + "z10_e1_BOTH*3,z10_Gate_DFU") + +(define_insn_reservation "z10_itoftd" 49 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "itoftd")) + "z10_e1_BOTH*3,z10_Gate_DFU") + +; Exact number of cycles is not known at compile-time. +(define_insn_reservation "z10_ftoidfp" 30 + (and (eq_attr "cpu" "z10") + (eq_attr "type" "ftoidfp")) + "z10_e1_BOTH*3,z10_Gate_DFU") + + +; +; Address-related bypasses +; + +; Here is the cycle diagram for address-related bypasses: +; ... G1 G2 G3 A0 A1 A2 A3 E1 P1 P2 P3 R0 ... +; ^ ^ ^ ^ ^ ^ +; | | | | | without bypass, its available AFTER this cycle +; | | | | E1-type bypasses provide the new value AFTER this cycle +; | | | A3-type bypasses provide the new value AFTER this cycle +; | | A1-type bypasses provide the new value AFTER this cycle +; | AGI resolution, actual USE of new value is DURING this cycle +; AGI detection + +(define_bypass 3 "z10_larl_A1, z10_la_fwd_A1, z10_other_fwd_A1, \ + z10_int_fwd_A1" + "z10_agen, z10_la, z10_branch, z10_call, z10_load, \ + z10_store, \ + z10_cs, z10_stm, z10_other" + "s390_agen_dep_p") + +(define_bypass 5 "z10_larl_fwd_A3, z10_load_fwd_A3, z10_other_fwd_A3, \ + z10_other_fr_A3, z10_int_fwd_A3, z10_int_fr_A3" + "z10_agen, z10_la, z10_branch, z10_call, z10_load, \ + z10_store, \ + z10_cs, z10_stm, z10_other" + "s390_agen_dep_p") + +(define_bypass 6 "z10_other_fr_E1, z10_other_super_c_E1, z10_other_super_E1, \ + z10_other_fwd_E1, \ + z10_lr_fr_E1, z10_larl_super_E1, \ + z10_int_super_E1, z10_int_fwd_E1, z10_int_fr_E1" + "z10_agen, z10_la, z10_branch, z10_call, z10_load, \ + z10_store, \ + z10_cs, z10_stm, z10_other" + "s390_agen_dep_p") + +(define_bypass 9 "z10_int_super, z10_int_fwd, z10_int_fr" + "z10_agen, z10_la, z10_branch, z10_call, z10_load, \ + z10_store, \ + z10_cs, z10_stm, z10_other" + "s390_agen_dep_p") + + + +; +; Try to avoid transitions between DFU-, BFU- and FXU-executed instructions as there is a +; dispatch delay required. +; + + +; Declaration for some pseudo-pipeline stages that reflect the +; dispatch gap when issueing an INT/FXU/BFU-executed instruction after +; an instruction executed by a different unit has been executed. The +; approach is that we pretend a pipelined execution of BFU operations +; with as many stages as the gap is long and request that none of +; these stages is busy when issueing a FXU- or DFU-executed +; instruction. Similar for FXU- and DFU-executed instructions. + +; Declaration for FPU stages. +(define_cpu_unit "z10_f0, z10_f1, z10_f2, z10_f3, z10_f4, z10_f5, z10_f6, \ + z10_f7, z10_f8, z10_f9, z10_f10, z10_f11, z10_f12" "z10_cpu") +(define_reservation "z10_FP_PP" "z10_f0, z10_f1, z10_f2, z10_f3, z10_f4, \ + z10_f5, z10_f6, z10_f7, z10_f8, z10_f9, z10_f10, z10_f11, \ + z10_f12") + +; Declaration for FXU stages. +(define_cpu_unit "z10_S1, z10_S2, z10_S3, z10_S4, z10_S5, z10_S6" "z10_cpu") +(define_cpu_unit "z10_T1, z10_T2, z10_T3, z10_T4, z10_T5, z10_T6" "z10_cpu") +(define_reservation "z10_INT_PP" "z10_S1 | z10_T1, z10_S2 | z10_T2, z10_S3 \ + | z10_T3, z10_S4 | z10_T4, z10_S5 | \ + z10_T5, z10_S6 | z10_T6") + +; Declaration for DFU stages. +(define_cpu_unit "z10_d0, z10_d1, z10_d2, z10_d3, z10_d4, z10_d5, z10_d6" + "z10_cpu") +(define_reservation "z10_DFU_PP" "z10_d0, z10_d1, z10_d2, z10_d3, z10_d4, \ + z10_d5, z10_d6") + + +; Pseudo-units representing whether the respective unit is available +; in the sense that using it does not cause a dispatch delay. + +(define_cpu_unit "z10_S_avail, z10_T_avail, z10_FP_avail, z10_DFU_avail" + "z10_cpu") + +(absence_set "z10_FP_avail" + "z10_S1, z10_S2, z10_S3, z10_S4, z10_S5, z10_S6, z10_T1, z10_T2, z10_T3, z10_T4, \ + z10_T5, z10_T6, \ + z10_d0, z10_d1, z10_d2, z10_d3, z10_d4, z10_d5, z10_d6") + +(absence_set "z10_S_avail,z10_T_avail" + "z10_f0, z10_f1, z10_f2, z10_f3, z10_f4, z10_f5, z10_f6, z10_f7, \ + z10_f8, z10_f9, z10_f10, z10_f11, z10_f12, \ + z10_d0, z10_d1, z10_d2, z10_d3, z10_d4, z10_d5, z10_d6") + +(absence_set "z10_DFU_avail" + "z10_S1, z10_S2, z10_S3, z10_S4, z10_S5, z10_S6, z10_T1, z10_T2, z10_T3, z10_T4, \ + z10_T5, z10_T6, \ + z10_f0, z10_f1, z10_f2, z10_f3, z10_f4, z10_f5, z10_f6, z10_f7, \ + z10_f8, z10_f9, z10_f10, z10_f11, z10_f12") + + +; Pseudo-units to be used in insn_reservations. + +(define_reservation "z10_Gate_ANY" "((z10_S_avail | z10_T_avail), z10_INT_PP)") +(define_reservation "z10_Gate_BOTH" "((z10_S_avail + z10_T_avail), z10_INT_PP)") + +(define_reservation "z10_Gate_FP" "z10_FP_avail, z10_FP_PP") + +(define_reservation "z10_Gate_DFU" "z10_DFU_avail, z10_DFU_PP") diff --git a/gcc/config/s390/2817.md b/gcc/config/s390/2817.md new file mode 100644 index 000000000..ea181b01f --- /dev/null +++ b/gcc/config/s390/2817.md @@ -0,0 +1,315 @@ +;; Scheduling description for z196 (cpu 2817). +;; Copyright (C) 2010 +;; Free Software Foundation, Inc. +;; Contributed by Christian Borntraeger (Christian.Borntraeger@de.ibm.com) +;; Andreas Krebbel (Andreas.Krebbel@de.ibm.com) + +;; 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/>. + +(define_automaton "z196_ipu") + +;; Fetch + Decoder +(define_cpu_unit "z196_g1" "z196_ipu") +(define_cpu_unit "z196_g2" "z196_ipu") +(define_cpu_unit "z196_g3" "z196_ipu") +(define_cpu_unit "z196_cr1" "z196_ipu") +(define_cpu_unit "z196_cr2" "z196_ipu") +(define_cpu_unit "z196_cr3" "z196_ipu") + +(final_presence_set "z196_g2" "z196_g1") +(final_presence_set "z196_g3" "z196_g2") +(final_presence_set "z196_cr2" "z196_cr1") +(final_presence_set "z196_cr3" "z196_cr2") +(exclusion_set "z196_g1" "z196_cr1") + +;; Instructions can be groupable, end a group, or be alone in a group. +(define_reservation "z196_simple" "( z196_g1 | z196_g2 | z196_g3 )") +(define_reservation "z196_ends" "( z196_g3 | ( z196_g2 + z196_g3 ) | ( z196_g1 + z196_g2 + z196_g3 ) )") + +;; Try to keep cracked and alone insns together in a clump. This will also +;; improve the clumping of "normal" insns. We also allow crackes insns +;; to go as a last instruction together with normal ones. +(define_reservation "z196_crack" "( z196_cr1 | z196_cr2 | z196_cr3 | z196_g3)") +(define_reservation "z196_alone" "( z196_cr1 | z196_cr2 | z196_cr3 )") + +;; Most simple instruction a fast enough to be handled by OOO even with +;; latency == 0. This reduces life ranges and spilling. We want to increase +;; life range for longer running ops, though, thats why we do not use +;; -fno-schedule-insns. +(define_insn_reservation "z196_simple_LSU" 0 + (and (eq_attr "cpu" "z196") + (and (eq_attr "type" "load,store,lr") + (eq_attr "z196prop" "none"))) + "z196_simple") + +(define_insn_reservation "z196_simple_FXU" 0 + (and (eq_attr "cpu" "z196") + (and (eq_attr "type" "integer,la,larl,other") + (and (eq_attr "z196prop" "none") + (eq_attr "op_type" "RR")))) + "z196_simple") + +(define_insn_reservation "z196_simple_DUAL" 0 + (and (eq_attr "cpu" "z196") + (and (eq_attr "type" "integer,la,larl,other") + (and (eq_attr "z196prop" "none") + (eq_attr "op_type" "!RR")))) + "z196_simple") + +(define_insn_reservation "z196_cracked" 0 + (and (eq_attr "cpu" "z196") + (and (eq_attr "type" "integer,la,larl,load,lr,store,other") + (eq_attr "z196prop" "z196_cracked"))) + "z196_crack") + +(define_insn_reservation "z196_alone" 0 + (and (eq_attr "cpu" "z196") + (and (eq_attr "type" "integer,la,larl,load,lr,store,other") + (eq_attr "z196prop" "z196_alone"))) + "z196_alone") + +(define_insn_reservation "z196_ends" 0 + (and (eq_attr "cpu" "z196") + (and (eq_attr "type" "integer,la,larl,load,lr,store,other") + (eq_attr "z196prop" "z196_ends"))) + "z196_ends") + +(define_insn_reservation "z196_branch" 0 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "branch")) + "z196_ends") + +(define_insn_reservation "z196_call" 0 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "jsr")) + "z196_ends") + +(define_insn_reservation "z196_mul_hi" 10 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "imulhi")) + "z196_simple") + +(define_insn_reservation "z196_mul_si" 12 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "imulsi")) + "z196_simple") + +(define_insn_reservation "z196_mul_di" 14 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "imuldi")) + "z196_simple") + +(define_insn_reservation "z196_div" 73 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "idiv")) + "z196_alone") + +(define_insn_reservation "z196_sem" 0 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "sem")) + "z196_crack") + +(define_insn_reservation "z196_cs" 0 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "cs")) + "z196_crack") + +(define_insn_reservation "z196_vs" 0 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "vs")) + "z196_alone") + +(define_insn_reservation "z196_lm_stm" 0 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "stm,lm")) + "z196_crack") + + +;; +;; Binary Floating Point +;; + +(define_insn_reservation "z196_fsimptf" 18 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fsimptf,fhex")) + "z196_alone") + +(define_insn_reservation "z196_fmultf" 47 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fmultf")) + "z196_alone") + +(define_insn_reservation "z196_fsimpdf" 7 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fsimpdf,fmuldf,fhex")) + "z196_simple") + +(define_insn_reservation "z196_fmadddf" 7 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fmadddf")) + "z196_alone") + +(define_insn_reservation "z196_fsimpsf" 7 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fsimpsf,fmulsf,fhex")) + "z196_simple") + +(define_insn_reservation "z196_fmaddsf" 7 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fmaddsf")) + "z196_alone") + +(define_insn_reservation "z196_fdivtf" 108 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fdivtf,fsqrttf")) + "z196_alone") + +(define_insn_reservation "z196_fdivdf" 36 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fdivdf,fsqrtdf")) + "z196_simple") + +(define_insn_reservation "z196_fdivsf" 29 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fdivsf,fsqrtsf")) + "z196_simple") + + +;; Loads and stores are cheap as well. +(define_insn_reservation "z196_floaddf" 0 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "floaddf")) + "z196_simple") + +(define_insn_reservation "z196_floadsf" 0 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "floadsf")) + "z196_simple") + +(define_insn_reservation "z196_fstoredf" 0 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fstoredf")) + "z196_simple") + +(define_insn_reservation "z196_fstoresf" 0 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fstoresf")) + "z196_simple") + + +(define_insn_reservation "z196_ftrunctf" 9 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "ftrunctf")) + "z196_simple") + +(define_insn_reservation "z196_ftruncdf" 7 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "ftruncdf")) + "z196_simple") + + +(define_insn_reservation "z196_ftoi" 7 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "ftoi")) + "z196_crack") + +(define_insn_reservation "z196_itof" 7 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "itoftf,itofdf,itofsf")) + "z196_crack") + +;; +;; Decimal Floating Point +;; + +;; DDTR +(define_insn_reservation "z196_fdivdd" 33 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fdivdd")) + "z196_simple") + +;; DXTR +(define_insn_reservation "z196_fdivtd" 35 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fdivtd")) + "z196_alone") + +;; LEDTR +(define_insn_reservation "z196_ftruncsd" 34 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "ftruncsd")) + "z196_simple") + +;; LDXTR +(define_insn_reservation "z196_ftruncdd" 36 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "ftruncdd")) + "z196_simple") + +;; These are normal fp loads/stores - which are cheap. +(define_insn_reservation "z196_floadsddd" 0 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "floadsd,floaddd,fstoredd,fstoresd")) + "z196_simple") + +;; MDTR +(define_insn_reservation "z196_fmuldd" 23 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fmuldd")) + "z196_simple") + +;; MXTR +(define_insn_reservation "z196_fmultd" 25 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fmultd")) + "z196_alone") + +;; multiple different isns like add, sub etc. +;; Just use the same defaults as z10. +(define_insn_reservation "z196_fsimpsd" 17 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fsimpsd")) + "z196_simple") +(define_insn_reservation "z196_fsimpdd" 17 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fsimpdd")) + "z196_simple") +(define_insn_reservation "z196_fsimptd" 18 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "fsimptd")) + "z196_alone") + +;; CDGTR +(define_insn_reservation "z196_itofdd" 45 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "itofdd")) + "z196_crack") + +;; CXGTR +(define_insn_reservation "z196_itoftd" 33 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "itoftd")) + "z196_crack") + +;; CGXTR, CGDTR +(define_insn_reservation "z196_ftoidfp" 33 + (and (eq_attr "cpu" "z196") + (eq_attr "type" "ftoidfp")) + "z196_crack") + + + diff --git a/gcc/config/s390/constraints.md b/gcc/config/s390/constraints.md new file mode 100644 index 000000000..8564b6619 --- /dev/null +++ b/gcc/config/s390/constraints.md @@ -0,0 +1,492 @@ +;; Constraints definitions belonging to the gcc backend for IBM S/390. +;; Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc. +;; Written by Wolfgang Gellerich, using code and information found in +;; files s390.md, s390.h, and s390.c. +;; +;; 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/>. + + +;; +;; Special constraints for s/390 machine description: +;; +;; a -- Any address register from 1 to 15. +;; b -- Memory operand whose address is a symbol reference or a symbol +;; reference + constant which can be proven to be naturally aligned. +;; c -- Condition code register 33. +;; d -- Any register from 0 to 15. +;; f -- Floating point registers. +;; t -- Access registers 36 and 37. +;; C -- A signed 8-bit constant (-128..127) +;; D -- An unsigned 16-bit constant (0..65535) +;; G -- Const double zero operand +;; I -- An 8-bit constant (0..255). +;; J -- A 12-bit constant (0..4095). +;; K -- A 16-bit constant (-32768..32767). +;; L -- Value appropriate as displacement. +;; (0..4095) for short displacement +;; (-524288..524287) for long displacement +;; M -- Constant integer with a value of 0x7fffffff. +;; N -- Multiple letter constraint followed by 4 parameter letters. +;; 0..9,x: number of the part counting from most to least significant +;; H,Q: mode of the part +;; D,S,H: mode of the containing operand +;; 0,F: value of the other parts (F - all bits set) +;; +;; The constraint matches if the specified part of a constant +;; has a value different from its other parts. If the letter x +;; is specified instead of a part number, the constraint matches +;; if there is any single part with non-default value. +;; O -- Multiple letter constraint followed by 1 parameter. +;; s: Signed extended immediate value (-2G .. 2G-1). +;; p: Positive extended immediate value (0 .. 4G-1). +;; n: Negative extended immediate value (-4G+1 .. -1). +;; These constraints do not accept any operand if the machine does +;; not provide the extended-immediate facility. +;; P -- Any integer constant that can be loaded without literal pool. +;; Q -- Memory reference without index register and with short displacement. +;; R -- Memory reference with index register and short displacement. +;; S -- Memory reference without index register but with long displacement. +;; T -- Memory reference with index register and long displacement. +;; A -- Multiple letter constraint followed by Q, R, S, or T: +;; Offsettable memory reference of type specified by second letter. +;; B -- Multiple letter constraint followed by Q, R, S, or T: +;; Memory reference of the type specified by second letter that +;; does *not* refer to a literal pool entry. +;; U -- Pointer with short displacement. (deprecated - use ZQZR) +;; W -- Pointer with long displacement. (deprecated - use ZSZT) +;; Y -- Shift count operand. +;; ZQ -- Pointer without index register and with short displacement. +;; ZR -- Pointer with index register and short displacement. +;; ZS -- Pointer without index register but with long displacement. +;; ZT -- Pointer with index register and long displacement. +;; +;; + + +;; +;; Register constraints. +;; + +(define_register_constraint "a" + "ADDR_REGS" + "Any address register from 1 to 15.") + + +(define_register_constraint "c" + "CC_REGS" + "Condition code register 33") + + +(define_register_constraint "d" + "GENERAL_REGS" + "Any register from 0 to 15") + + +(define_register_constraint "f" + "FP_REGS" + "Floating point registers") + + +(define_register_constraint "t" + "ACCESS_REGS" + "@internal + Access registers 36 and 37") + + +;; +;; General constraints for constants. +;; + +(define_constraint "C" + "@internal + An 8-bit signed immediate constant (-128..127)" + (and (match_code "const_int") + (match_test "ival >= -128 && ival <= 127"))) + + +(define_constraint "D" + "An unsigned 16-bit constant (0..65535)" + (and (match_code "const_int") + (match_test "ival >= 0 && ival <= 65535"))) + + +(define_constraint "G" + "@internal + Const double zero operand" + (and (match_code "const_double") + (match_test "s390_float_const_zero_p (op)"))) + + +(define_constraint "I" + "An 8-bit constant (0..255)" + (and (match_code "const_int") + (match_test "(unsigned HOST_WIDE_INT) ival <= 255"))) + + +(define_constraint "J" + "A 12-bit constant (0..4095)" + (and (match_code "const_int") + (match_test "(unsigned HOST_WIDE_INT) ival <= 4095"))) + + +(define_constraint "K" + "A 16-bit constant (-32768..32767)" + (and (match_code "const_int") + (match_test "ival >= -32768 && ival <= 32767"))) + + +(define_constraint "L" + "Value appropriate as displacement. + (0..4095) for short displacement + (-524288..524287) for long displacement" + (and (match_code "const_int") + (match_test "TARGET_LONG_DISPLACEMENT ? + (ival >= -524288 && ival <= 524287) + : (ival >= 0 && ival <= 4095)"))) + + +(define_constraint "M" + "Constant integer with a value of 0x7fffffff" + (and (match_code "const_int") + (match_test "ival == 2147483647"))) + + +(define_constraint "P" + "@internal + Any integer constant that can be loaded without literal pool" + (and (match_code "const_int") + (match_test "legitimate_reload_constant_p (GEN_INT (ival))"))) + + +(define_address_constraint "Y" + "Shift count operand" + +;; Simply check for the basic form of a shift count. Reload will +;; take care of making sure we have a proper base register. + + (match_test "s390_decompose_shift_count (op, NULL, NULL)" )) + + +;; N -- Multiple letter constraint followed by 4 parameter letters. +;; 0..9,x: number of the part counting from most to least significant +;; H,Q: mode of the part +;; D,S,H: mode of the containing operand +;; 0,F: value of the other parts (F = all bits set) +;; +;; The constraint matches if the specified part of a constant +;; has a value different from its other parts. If the letter x +;; is specified instead of a part number, the constraint matches +;; if there is any single part with non-default value. +;; +;; The following patterns define only those constraints that are actually +;; used in s390.md. If you need an additional one, simply add it in the +;; obvious way. Function s390_N_constraint_str is ready to handle all +;; combinations. +;; + + +(define_constraint "NxQS0" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"xQS0\", ival)"))) + + +(define_constraint "NxQD0" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"xQD0\", ival)"))) + + +(define_constraint "N3HD0" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"3HD0\", ival)"))) + + +(define_constraint "N2HD0" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"2HD0\", ival)"))) + + +(define_constraint "N1SD0" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"1SD0\", ival)"))) + + +(define_constraint "N1HS0" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"1HS0\", ival)"))) + + +(define_constraint "N1HD0" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"1HD0\", ival)"))) + + +(define_constraint "N0SD0" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"0SD0\", ival)"))) + + +(define_constraint "N0HS0" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"0HS0\", ival)"))) + + +(define_constraint "N0HD0" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"0HD0\", ival)"))) + + +(define_constraint "NxQDF" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"xQDF\", ival)"))) + + +(define_constraint "N1SDF" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"1SDF\", ival)"))) + + +(define_constraint "N0SDF" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"0SDF\", ival)"))) + + +(define_constraint "N3HDF" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"3HDF\", ival)"))) + + +(define_constraint "N2HDF" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"2HDF\", ival)"))) + + +(define_constraint "N1HDF" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"1HDF\", ival)"))) + + +(define_constraint "N0HDF" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"0HDF\", ival)"))) + + +(define_constraint "N0HSF" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"0HSF\", ival)"))) + + +(define_constraint "N1HSF" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"1HSF\", ival)"))) + + +(define_constraint "NxQSF" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"xQSF\", ival)"))) + + +(define_constraint "NxQHF" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"xQHF\", ival)"))) + + +(define_constraint "NxQH0" + "@internal" + (and (match_code "const_int") + (match_test "s390_N_constraint_str (\"xQH0\", ival)"))) + + + + +;; +;; Double-letter constraints starting with O follow. +;; + + +(define_constraint "Os" + "@internal + Signed extended immediate value (-2G .. 2G-1). + This constraint will only match if the machine provides + the extended-immediate facility." + (and (match_code "const_int") + (match_test "s390_O_constraint_str ('s', ival)"))) + + +(define_constraint "Op" + "@internal + Positive extended immediate value (0 .. 4G-1). + This constraint will only match if the machine provides + the extended-immediate facility." + (and (match_code "const_int") + (match_test "s390_O_constraint_str ('p', ival)"))) + + +(define_constraint "On" + "@internal + Negative extended immediate value (-4G+1 .. -1). + This constraint will only match if the machine provides + the extended-immediate facility." + (and (match_code "const_int") + (match_test "s390_O_constraint_str ('n', ival)"))) + + + + +;; +;; Memory constraints follow. +;; + +(define_memory_constraint "Q" + "Memory reference without index register and with short displacement" + (match_test "s390_mem_constraint (\"Q\", op)")) + + +(define_memory_constraint "R" + "Memory reference with index register and short displacement" + (match_test "s390_mem_constraint (\"R\", op)")) + + +(define_memory_constraint "S" + "Memory reference without index register but with long displacement" + (match_test "s390_mem_constraint (\"S\", op)")) + + +(define_memory_constraint "T" + "Memory reference with index register and long displacement" + (match_test "s390_mem_constraint (\"T\", op)")) + + +(define_memory_constraint "b" + "Memory reference whose address is a naturally aligned symbol reference." + (match_test "MEM_P (op) + && s390_check_symref_alignment (XEXP (op, 0), + GET_MODE_SIZE (GET_MODE (op)))")) + +(define_memory_constraint "e" + "Matches all memory references available on the current architecture +level. This constraint will never be used and using it in an inline +assembly is *always* a bug since there is no instruction accepting all +those addresses. It just serves as a placeholder for a generic memory +constraint." + (match_test "strict_memory_address_p (GET_MODE (op), op)")) + +; This defines 'm' as normal memory constraint. This is only possible +; since the standard memory constraint is re-defined in s390.h using +; the TARGET_MEM_CONSTRAINT macro. +(define_memory_constraint "m" + "Matches the most general memory address for pre-z10 machines." + (match_test "s390_mem_constraint (\"R\", op) + || s390_mem_constraint (\"T\", op)")) + +(define_memory_constraint "AQ" + "@internal + Offsettable memory reference without index register and with short displacement" + (match_test "s390_mem_constraint (\"AQ\", op)")) + + +(define_memory_constraint "AR" + "@internal + Offsettable memory reference with index register and short displacement" + (match_test "s390_mem_constraint (\"AR\", op)")) + + +(define_memory_constraint "AS" + "@internal + Offsettable memory reference without index register but with long displacement" + (match_test "s390_mem_constraint (\"AS\", op)")) + + +(define_memory_constraint "AT" + "@internal + Offsettable memory reference with index register and long displacement" + (match_test "s390_mem_constraint (\"AT\", op)")) + + + +(define_constraint "BQ" + "@internal + Memory reference without index register and with short + displacement that does *not* refer to a literal pool entry." + (match_test "s390_mem_constraint (\"BQ\", op)")) + + +(define_constraint "BR" + "@internal + Memory reference with index register and short displacement that + does *not* refer to a literal pool entry. " + (match_test "s390_mem_constraint (\"BR\", op)")) + + +(define_constraint "BS" + "@internal + Memory reference without index register but with long displacement + that does *not* refer to a literal pool entry. " + (match_test "s390_mem_constraint (\"BS\", op)")) + + +(define_constraint "BT" + "@internal + Memory reference with index register and long displacement that + does *not* refer to a literal pool entry. " + (match_test "s390_mem_constraint (\"BT\", op)")) + + +(define_address_constraint "U" + "Pointer with short displacement. (deprecated - use ZQZR)" + (match_test "s390_mem_constraint (\"U\", op)")) + +(define_address_constraint "W" + "Pointer with long displacement. (deprecated - use ZSZT)" + (match_test "s390_mem_constraint (\"W\", op)")) + + +(define_address_constraint "ZQ" + "Pointer without index register and with short displacement." + (match_test "s390_mem_constraint (\"ZQ\", op)")) + +(define_address_constraint "ZR" + "Pointer with index register and short displacement." + (match_test "s390_mem_constraint (\"ZR\", op)")) + +(define_address_constraint "ZS" + "Pointer without index register but with long displacement." + (match_test "s390_mem_constraint (\"ZS\", op)")) + +(define_address_constraint "ZT" + "Pointer with index register and long displacement." + (match_test "s390_mem_constraint (\"ZT\", op)")) diff --git a/gcc/config/s390/linux-unwind.h b/gcc/config/s390/linux-unwind.h new file mode 100644 index 000000000..558087fad --- /dev/null +++ b/gcc/config/s390/linux-unwind.h @@ -0,0 +1,130 @@ +/* DWARF2 EH unwinding support for S/390 Linux. + Copyright (C) 2004, 2005, 2006, 2009 Free Software Foundation, Inc. + +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. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +/* Do code reading to identify a signal frame, and set the frame + state data appropriately. See unwind-dw2.c for the structs. */ + +#define MD_FALLBACK_FRAME_STATE_FOR s390_fallback_frame_state + +static _Unwind_Reason_Code +s390_fallback_frame_state (struct _Unwind_Context *context, + _Unwind_FrameState *fs) +{ + unsigned char *pc = context->ra; + long new_cfa; + int i; + + typedef struct + { + unsigned long psw_mask; + unsigned long psw_addr; + unsigned long gprs[16]; + unsigned int acrs[16]; + unsigned int fpc; + unsigned int __pad; + double fprs[16]; + } __attribute__ ((__aligned__ (8))) sigregs_; + + sigregs_ *regs; + int *signo; + + /* svc $__NR_sigreturn or svc $__NR_rt_sigreturn */ + if (pc[0] != 0x0a || (pc[1] != 119 && pc[1] != 173)) + return _URC_END_OF_STACK; + + /* Legacy frames: + old signal mask (8 bytes) + pointer to sigregs (8 bytes) - points always to next location + sigregs + retcode + This frame layout was used on kernels < 2.6.9 for non-RT frames, + and on kernels < 2.4.13 for RT frames as well. Note that we need + to look at RA to detect this layout -- this means that if you use + sa_restorer to install a different signal restorer on a legacy + kernel, unwinding from signal frames will not work. */ + if (context->ra == context->cfa + 16 + sizeof (sigregs_)) + { + regs = (sigregs_ *)(context->cfa + 16); + signo = NULL; + } + + /* New-style RT frame: + retcode + alignment (8 bytes) + siginfo (128 bytes) + ucontext (contains sigregs) */ + else if (pc[1] == 173 /* __NR_rt_sigreturn */) + { + struct ucontext_ + { + unsigned long uc_flags; + struct ucontext_ *uc_link; + unsigned long uc_stack[3]; + sigregs_ uc_mcontext; + } *uc = context->cfa + 8 + 128; + + regs = &uc->uc_mcontext; + signo = context->cfa + sizeof(long); + } + + /* New-style non-RT frame: + old signal mask (8 bytes) + pointer to sigregs (followed by signal number) */ + else + { + regs = *(sigregs_ **)(context->cfa + 8); + signo = (int *)(regs + 1); + } + + new_cfa = regs->gprs[15] + 16*sizeof(long) + 32; + fs->regs.cfa_how = CFA_REG_OFFSET; + fs->regs.cfa_reg = 15; + fs->regs.cfa_offset = + new_cfa - (long) context->cfa + 16*sizeof(long) + 32; + + for (i = 0; i < 16; i++) + { + fs->regs.reg[i].how = REG_SAVED_OFFSET; + fs->regs.reg[i].loc.offset = + (long)®s->gprs[i] - new_cfa; + } + for (i = 0; i < 16; i++) + { + fs->regs.reg[16+i].how = REG_SAVED_OFFSET; + fs->regs.reg[16+i].loc.offset = + (long)®s->fprs[i] - new_cfa; + } + + /* Load return addr from PSW into dummy register 32. */ + + fs->regs.reg[32].how = REG_SAVED_OFFSET; + fs->regs.reg[32].loc.offset = (long)®s->psw_addr - new_cfa; + fs->retaddr_column = 32; + /* SIGILL, SIGFPE and SIGTRAP are delivered with psw_addr + after the faulting instruction rather than before it. + Don't set FS->signal_frame in that case. */ + if (!signo || (*signo != 4 && *signo != 5 && *signo != 8)) + fs->signal_frame = 1; + + return _URC_NO_REASON; +} diff --git a/gcc/config/s390/linux.h b/gcc/config/s390/linux.h new file mode 100644 index 000000000..95cead119 --- /dev/null +++ b/gcc/config/s390/linux.h @@ -0,0 +1,104 @@ +/* Definitions for Linux for S/390. + Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2010 + Free Software Foundation, Inc. + Contributed by Hartmut Penner (hpenner@de.ibm.com) and + Ulrich Weigand (uweigand@de.ibm.com). + +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/>. */ + +#ifndef _LINUX_H +#define _LINUX_H + +/* Target specific version string. */ + +#ifdef DEFAULT_TARGET_64BIT +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (Linux for zSeries)"); +#else +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (Linux for S/390)"); +#endif + + +/* Target specific type definitions. */ + +/* ??? Do we really want long as size_t on 31-bit? */ +#undef SIZE_TYPE +#define SIZE_TYPE (TARGET_64BIT ? "long unsigned int" : "long unsigned int") +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE (TARGET_64BIT ? "long int" : "int") + +#undef WCHAR_TYPE +#define WCHAR_TYPE "int" +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE 32 + + +/* Target specific preprocessor settings. */ + +#define TARGET_OS_CPP_BUILTINS() \ + do \ + { \ + LINUX_TARGET_OS_CPP_BUILTINS(); \ + } \ + while (0) + + +/* Target specific assembler settings. */ + +#undef ASM_SPEC +#define ASM_SPEC "%{m31&m64}%{mesa&mzarch}%{march=*}" + + +/* Target specific linker settings. */ + +#ifdef DEFAULT_TARGET_64BIT +#define MULTILIB_DEFAULTS { "m64" } +#else +#define MULTILIB_DEFAULTS { "m31" } +#endif + +#define GLIBC_DYNAMIC_LINKER32 "/lib/ld.so.1" +#define GLIBC_DYNAMIC_LINKER64 "/lib/ld64.so.1" + +#undef LINK_SPEC +#define LINK_SPEC \ + "%{m31:-m elf_s390}%{m64:-m elf64_s390} \ + %{shared:-shared} \ + %{!shared: \ + %{static:-static} \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + %{m31:-dynamic-linker " LINUX_DYNAMIC_LINKER32 "} \ + %{m64:-dynamic-linker " LINUX_DYNAMIC_LINKER64 "}}}" + +#define CPP_SPEC "%{posix:-D_POSIX_SOURCE} %{pthread:-D_REENTRANT}" + +#define TARGET_ASM_FILE_END file_end_indicate_exec_stack + +#define MD_UNWIND_SUPPORT "config/s390/linux-unwind.h" + +#ifdef TARGET_LIBC_PROVIDES_SSP +/* s390 glibc provides __stack_chk_guard in 0x14(tp), + s390x glibc provides it at 0x28(tp). */ +#define TARGET_THREAD_SSP_OFFSET (TARGET_64BIT ? 0x28 : 0x14) +#endif + +/* Define if long doubles should be mangled as 'g'. */ +#define TARGET_ALTERNATE_LONG_DOUBLE_MANGLING + +#endif diff --git a/gcc/config/s390/predicates.md b/gcc/config/s390/predicates.md new file mode 100644 index 000000000..9d619fbc0 --- /dev/null +++ b/gcc/config/s390/predicates.md @@ -0,0 +1,406 @@ +;; Predicate definitions for S/390 and zSeries. +;; Copyright (C) 2005, 2007, 2008 Free Software Foundation, Inc. +;; Contributed by Hartmut Penner (hpenner@de.ibm.com) and +;; Ulrich Weigand (uweigand@de.ibm.com). +;; +;; 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/>. + +;; OP is the current operation. +;; MODE is the current operation mode. + +;; operands -------------------------------------------------------------- + +;; Return true if OP a (const_int 0) operand. + +(define_predicate "const0_operand" + (and (match_code "const_int, const_double") + (match_test "op == CONST0_RTX (mode)"))) + +;; Return true if OP is constant. + +(define_special_predicate "consttable_operand" + (and (match_code "symbol_ref, label_ref, const, const_int, const_double") + (match_test "CONSTANT_P (op)"))) + +;; Return true if OP is a valid S-type operand. + +(define_predicate "s_operand" + (and (match_code "subreg, mem") + (match_operand 0 "general_operand")) +{ + /* Just like memory_operand, allow (subreg (mem ...)) + after reload. */ + if (reload_completed + && GET_CODE (op) == SUBREG + && GET_CODE (SUBREG_REG (op)) == MEM) + op = SUBREG_REG (op); + + if (GET_CODE (op) != MEM) + return false; + if (!s390_legitimate_address_without_index_p (op)) + return false; + + return true; +}) + +;; Return true if OP is a valid operand for the BRAS instruction. +;; Allow SYMBOL_REFs and @PLT stubs. + +(define_special_predicate "bras_sym_operand" + (ior (and (match_code "symbol_ref") + (match_test "!flag_pic || SYMBOL_REF_LOCAL_P (op)")) + (and (match_code "const") + (and (match_test "GET_CODE (XEXP (op, 0)) == UNSPEC") + (match_test "XINT (XEXP (op, 0), 1) == UNSPEC_PLT"))))) + +;; Return true if OP is a PLUS that is not a legitimate +;; operand for the LA instruction. + +(define_predicate "s390_plus_operand" + (and (match_code "plus") + (and (match_test "mode == Pmode") + (match_test "!legitimate_la_operand_p (op)")))) + +;; Return true if OP is a valid operand as shift count or setmem. + +(define_predicate "shift_count_or_setmem_operand" + (match_code "reg, subreg, plus, const_int") +{ + HOST_WIDE_INT offset; + rtx base; + + /* Extract base register and offset. */ + if (!s390_decompose_shift_count (op, &base, &offset)) + return false; + + /* Don't allow any non-base hard registers. Doing so without + confusing reload and/or regrename would be tricky, and doesn't + buy us much anyway. */ + if (base && REGNO (base) < FIRST_PSEUDO_REGISTER && !ADDR_REG_P (base)) + return false; + + /* Unfortunately we have to reject constants that are invalid + for an address, or else reload will get confused. */ + if (!DISP_IN_RANGE (offset)) + return false; + + return true; +}) + +;; Return true if OP a valid operand for the LARL instruction. + +(define_predicate "larl_operand" + (match_code "label_ref, symbol_ref, const, const_int, const_double") +{ + /* Allow labels and local symbols. */ + if (GET_CODE (op) == LABEL_REF) + return true; + if (GET_CODE (op) == SYMBOL_REF) + return (!SYMBOL_REF_ALIGN1_P (op) + && SYMBOL_REF_TLS_MODEL (op) == 0 + && (!flag_pic || SYMBOL_REF_LOCAL_P (op))); + + /* Everything else must have a CONST, so strip it. */ + if (GET_CODE (op) != CONST) + return false; + op = XEXP (op, 0); + + /* Allow adding *even* in-range constants. */ + if (GET_CODE (op) == PLUS) + { + if (GET_CODE (XEXP (op, 1)) != CONST_INT + || (INTVAL (XEXP (op, 1)) & 1) != 0) + return false; + if (INTVAL (XEXP (op, 1)) >= (HOST_WIDE_INT)1 << 31 + || INTVAL (XEXP (op, 1)) < -((HOST_WIDE_INT)1 << 31)) + return false; + op = XEXP (op, 0); + } + + /* Labels and local symbols allowed here as well. */ + if (GET_CODE (op) == LABEL_REF) + return true; + if (GET_CODE (op) == SYMBOL_REF) + return ((SYMBOL_REF_FLAGS (op) & SYMBOL_FLAG_ALIGN1) == 0 + && SYMBOL_REF_TLS_MODEL (op) == 0 + && (!flag_pic || SYMBOL_REF_LOCAL_P (op))); + + /* Now we must have a @GOTENT offset or @PLT stub + or an @INDNTPOFF TLS offset. */ + if (GET_CODE (op) == UNSPEC + && XINT (op, 1) == UNSPEC_GOTENT) + return true; + if (GET_CODE (op) == UNSPEC + && XINT (op, 1) == UNSPEC_PLT) + return true; + if (GET_CODE (op) == UNSPEC + && XINT (op, 1) == UNSPEC_INDNTPOFF) + return true; + + return false; +}) + +;; operators -------------------------------------------------------------- + +;; Return nonzero if OP is a valid comparison operator +;; for a branch condition. + +(define_predicate "s390_comparison" + (match_code "eq, ne, lt, gt, le, ge, ltu, gtu, leu, geu, + uneq, unlt, ungt, unle, unge, ltgt, + unordered, ordered") +{ + if (GET_CODE (XEXP (op, 0)) != REG + || REGNO (XEXP (op, 0)) != CC_REGNUM + || XEXP (op, 1) != const0_rtx) + return false; + + return (s390_branch_condition_mask (op) >= 0); +}) + +;; Return true if op is the cc register. +(define_predicate "cc_reg_operand" + (and (match_code "reg") + (match_test "REGNO (op) == CC_REGNUM"))) + +(define_predicate "s390_signed_integer_comparison" + (match_code "eq, ne, lt, gt, le, ge") +{ + return (s390_compare_and_branch_condition_mask (op) >= 0); +}) + +(define_predicate "s390_unsigned_integer_comparison" + (match_code "eq, ne, ltu, gtu, leu, geu") +{ + return (s390_compare_and_branch_condition_mask (op) >= 0); +}) + +;; Return nonzero if OP is a valid comparison operator for the +;; cstore expanders -- respectively cstorecc4 and integer cstore. +(define_predicate "s390_eqne_operator" + (match_code "eq, ne")) + +(define_predicate "s390_scond_operator" + (match_code "ltu, gtu, leu, geu")) + +(define_predicate "s390_brx_operator" + (match_code "le, gt")) + +;; Return nonzero if OP is a valid comparison operator +;; for an ALC condition. + +(define_predicate "s390_alc_comparison" + (match_code "zero_extend, sign_extend, ltu, gtu, leu, geu") +{ + while (GET_CODE (op) == ZERO_EXTEND || GET_CODE (op) == SIGN_EXTEND) + op = XEXP (op, 0); + + if (!COMPARISON_P (op)) + return false; + + if (GET_CODE (XEXP (op, 0)) != REG + || REGNO (XEXP (op, 0)) != CC_REGNUM + || XEXP (op, 1) != const0_rtx) + return false; + + switch (GET_MODE (XEXP (op, 0))) + { + case CCL1mode: + return GET_CODE (op) == LTU; + + case CCL2mode: + return GET_CODE (op) == LEU; + + case CCL3mode: + return GET_CODE (op) == GEU; + + case CCUmode: + return GET_CODE (op) == GTU; + + case CCURmode: + return GET_CODE (op) == LTU; + + case CCSmode: + return GET_CODE (op) == UNGT; + + case CCSRmode: + return GET_CODE (op) == UNLT; + + default: + return false; + } +}) + +;; Return nonzero if OP is a valid comparison operator +;; for an SLB condition. + +(define_predicate "s390_slb_comparison" + (match_code "zero_extend, sign_extend, ltu, gtu, leu, geu") +{ + while (GET_CODE (op) == ZERO_EXTEND || GET_CODE (op) == SIGN_EXTEND) + op = XEXP (op, 0); + + if (!COMPARISON_P (op)) + return false; + + if (GET_CODE (XEXP (op, 0)) != REG + || REGNO (XEXP (op, 0)) != CC_REGNUM + || XEXP (op, 1) != const0_rtx) + return false; + + switch (GET_MODE (XEXP (op, 0))) + { + case CCL1mode: + return GET_CODE (op) == GEU; + + case CCL2mode: + return GET_CODE (op) == GTU; + + case CCL3mode: + return GET_CODE (op) == LTU; + + case CCUmode: + return GET_CODE (op) == LEU; + + case CCURmode: + return GET_CODE (op) == GEU; + + case CCSmode: + return GET_CODE (op) == LE; + + case CCSRmode: + return GET_CODE (op) == GE; + + default: + return false; + } +}) + +;; Return true if OP is a load multiple operation. It is known to be a +;; PARALLEL and the first section will be tested. + +(define_special_predicate "load_multiple_operation" + (match_code "parallel") +{ + enum machine_mode elt_mode; + int count = XVECLEN (op, 0); + unsigned int dest_regno; + rtx src_addr; + int i, off; + + /* Perform a quick check so we don't blow up below. */ + if (count <= 1 + || GET_CODE (XVECEXP (op, 0, 0)) != SET + || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG + || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM) + return false; + + dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0))); + src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0); + elt_mode = GET_MODE (SET_DEST (XVECEXP (op, 0, 0))); + + /* Check, is base, or base + displacement. */ + + if (GET_CODE (src_addr) == REG) + off = 0; + else if (GET_CODE (src_addr) == PLUS + && GET_CODE (XEXP (src_addr, 0)) == REG + && GET_CODE (XEXP (src_addr, 1)) == CONST_INT) + { + off = INTVAL (XEXP (src_addr, 1)); + src_addr = XEXP (src_addr, 0); + } + else + return false; + + for (i = 1; i < count; i++) + { + rtx elt = XVECEXP (op, 0, i); + + if (GET_CODE (elt) != SET + || GET_CODE (SET_DEST (elt)) != REG + || GET_MODE (SET_DEST (elt)) != elt_mode + || REGNO (SET_DEST (elt)) != dest_regno + i + || GET_CODE (SET_SRC (elt)) != MEM + || GET_MODE (SET_SRC (elt)) != elt_mode + || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS + || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr) + || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT + || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1)) + != off + i * GET_MODE_SIZE (elt_mode)) + return false; + } + + return true; +}) + +;; Return true if OP is a store multiple operation. It is known to be a +;; PARALLEL and the first section will be tested. + +(define_special_predicate "store_multiple_operation" + (match_code "parallel") +{ + enum machine_mode elt_mode; + int count = XVECLEN (op, 0); + unsigned int src_regno; + rtx dest_addr; + int i, off; + + /* Perform a quick check so we don't blow up below. */ + if (count <= 1 + || GET_CODE (XVECEXP (op, 0, 0)) != SET + || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM + || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG) + return false; + + src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0))); + dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0); + elt_mode = GET_MODE (SET_SRC (XVECEXP (op, 0, 0))); + + /* Check, is base, or base + displacement. */ + + if (GET_CODE (dest_addr) == REG) + off = 0; + else if (GET_CODE (dest_addr) == PLUS + && GET_CODE (XEXP (dest_addr, 0)) == REG + && GET_CODE (XEXP (dest_addr, 1)) == CONST_INT) + { + off = INTVAL (XEXP (dest_addr, 1)); + dest_addr = XEXP (dest_addr, 0); + } + else + return false; + + for (i = 1; i < count; i++) + { + rtx elt = XVECEXP (op, 0, i); + + if (GET_CODE (elt) != SET + || GET_CODE (SET_SRC (elt)) != REG + || GET_MODE (SET_SRC (elt)) != elt_mode + || REGNO (SET_SRC (elt)) != src_regno + i + || GET_CODE (SET_DEST (elt)) != MEM + || GET_MODE (SET_DEST (elt)) != elt_mode + || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS + || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr) + || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT + || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1)) + != off + i * GET_MODE_SIZE (elt_mode)) + return false; + } + return true; +}) diff --git a/gcc/config/s390/s390-modes.def b/gcc/config/s390/s390-modes.def new file mode 100644 index 000000000..be2bf6ea7 --- /dev/null +++ b/gcc/config/s390/s390-modes.def @@ -0,0 +1,174 @@ +/* Definitions of target machine for GNU compiler, for IBM S/390 + Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc. + Contributed by Hartmut Penner (hpenner@de.ibm.com) and + Ulrich Weigand (uweigand@de.ibm.com). + +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/>. */ + +/* 256-bit integer mode is needed for STACK_SAVEAREA_MODE. */ +INT_MODE (OI, 32); + +/* Define TFmode to work around reload problem PR 20927. */ +FLOAT_MODE (TF, 16, ieee_quad_format); + +/* Add any extra modes needed to represent the condition code. */ + +/* + +Condition Codes + +Check for zero + +CCZ: EQ NE NE NE +CCZ1: EQ NE (CS) + +Unsigned compares + +CCU: EQ LTU GTU NE (CLG/R, CL/R/Y, CLM/Y, CLI/Y) +CCUR: EQ GTU LTU NE (CLGF/R) + +Signed compares + +CCS: EQ LT GT UNORDERED (LTGFR, LTGR, LTR, ICM/Y, + LTDBR, LTDR, LTEBR, LTER, + CG/R, C/R/Y, CGHI, CHI, + CDB/R, CD/R, CEB/R, CE/R, + ADB/R, AEB/R, SDB/R, SEB/R, + SRAG, SRA, SRDA) +CCSR: EQ GT LT UNORDERED (CGF/R, CH/Y) + +Condition codes resulting from add with overflow + +CCA: EQ LT GT Overflow +CCAP: EQ LT GT LT (AGHI, AHI) +CCAN: EQ LT GT GT (AGHI, AHI) + +Condition codes of unsigned adds and subs + +CCL: EQ NE EQ NE (ALGF/R, ALG/R, AL/R/Y, + ALCG/R, ALC/R, + SLGF/R, SLG/R, SL/R/Y, + SLBG/R, SLB/R) +CCL1: GEU GEU LTU LTU (ALG/R, AL/R/Y) +CCL2: GTU GTU LEU LEU (SLG/R, SL/R/Y) +CCL3: EQ LTU EQ GTU (SLG/R, SL/R/Y) + +Test under mask checks + +CCT: EQ NE NE NE (ICM/Y, TML, CG/R, CGHI, + C/R/Y, CHI, NG/R, N/R/Y, + OG/R, O/R/Y, XG/R, X/R/Y) +CCT1: NE EQ NE NE (TMH, TML) +CCT2: NE NE EQ NE (TMH, TML) +CCT3: NE NE NE EQ (TMH, TML) + +CCA and CCT modes are request only modes. These modes are never returned by +s390_select_cc_mode. They are only intended to match other modes. + +Requested mode -> Destination CC register mode + +CCS, CCU, CCT, CCSR, CCUR -> CCZ +CCA -> CCAP, CCAN + + +*** Comments *** + +CCAP, CCAN + +The CC obtained from add instruction usually can't be used for comparisons +because its coupling with overflow flag. In case of an overflow the +less than/greater than data are lost. Nevertheless a comparison can be done +whenever immediate values are involved because they are known at compile time. +If you know whether the used constant is positive or negative you can predict +the sign of the result even in case of an overflow. + + +CCT, CCT1, CCT2, CCT3 + +If bits of an integer masked with an AND instruction are checked, the test under +mask instructions turn out to be very handy for a set of special cases. +The simple cases are checks whether all masked bits are zero or ones: + + int a; + if ((a & (16 + 128)) == 0) -> CCT/CCZ + if ((a & (16 + 128)) == 16 + 128) -> CCT3 + +Using two extra modes makes it possible to do complete checks on two bits of an +integer (This is possible on register operands only. TM does not provide the +information necessary for CCT1 and CCT2 modes.): + + int a; + if ((a & (16 + 128)) == 16) -> CCT1 + if ((a & (16 + 128)) == 128) -> CCT2 + + +CCSR, CCUR + +There are several instructions comparing 32 bit with 64-bit unsigned/signed +values. Such instructions can be considered to have a builtin zero/sign_extend. +The problem is that in the RTL (to be canonical) the zero/sign extended operand +has to be the first one but the machine instructions like it the other way +around. The following both modes can be considered as CCS and CCU modes with +exchanged operands. + + +CCL1, CCL2 + +These modes represent the result of overflow checks. + +if (a + b < a) -> CCL1 state of the carry bit (CC2 | CC3) +if (a - b > a) -> CCL2 state of the borrow bit (CC0 | CC1) + +They are used when multi word numbers are computed dealing one SImode part after +another or whenever manual overflow checks like the examples above are +compiled. + + +CCL3 + +A logical subtract instruction sets the borrow bit in case of an overflow. +The resulting condition code of those instructions is represented by the +CCL3 mode. Together with the CCU mode this mode is used for jumpless +implementations of several if-constructs - see s390_expand_addcc for more +details. + +CCZ1 + +The compare and swap instructions sets the condition code to 0/1 if the +operands were equal/unequal. The CCZ1 mode ensures the result can be +effectively placed into a register. + +*/ + + +CC_MODE (CCZ); +CC_MODE (CCZ1); +CC_MODE (CCA); +CC_MODE (CCAP); +CC_MODE (CCAN); +CC_MODE (CCL); +CC_MODE (CCL1); +CC_MODE (CCL2); +CC_MODE (CCL3); +CC_MODE (CCU); +CC_MODE (CCUR); +CC_MODE (CCS); +CC_MODE (CCSR); +CC_MODE (CCT); +CC_MODE (CCT1); +CC_MODE (CCT2); +CC_MODE (CCT3); diff --git a/gcc/config/s390/s390-protos.h b/gcc/config/s390/s390-protos.h new file mode 100644 index 000000000..399d75715 --- /dev/null +++ b/gcc/config/s390/s390-protos.h @@ -0,0 +1,114 @@ +/* Definitions of target machine for GNU compiler, for IBM S/390. + Copyright (C) 2000, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011 + Free Software Foundation, Inc. + + Contributed by Hartmut Penner (hpenner@de.ibm.com) + +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/>. */ + + + +/* Prototypes of functions used for constraint evaluation in + constraints.c. */ + +extern int s390_mem_constraint (const char *str, rtx op); +extern int s390_O_constraint_str (const char c, HOST_WIDE_INT value); +extern int s390_N_constraint_str (const char *str, HOST_WIDE_INT value); +extern int s390_float_const_zero_p (rtx value); +extern bool s390_check_symref_alignment (rtx addr, HOST_WIDE_INT alignment); + + +/* Declare functions in s390.c. */ + +extern HOST_WIDE_INT s390_initial_elimination_offset (int, int); +extern void s390_emit_prologue (void); +extern void s390_emit_epilogue (bool); +extern void s390_function_profiler (FILE *, int); +extern void s390_set_has_landing_pad_p (bool); +extern bool s390_hard_regno_mode_ok (unsigned int, enum machine_mode); +extern bool s390_hard_regno_rename_ok (unsigned int, unsigned int); +extern int s390_class_max_nregs (enum reg_class, enum machine_mode); + +#ifdef RTX_CODE +extern int s390_extra_constraint_str (rtx, int, const char *); +extern int s390_const_ok_for_constraint_p (HOST_WIDE_INT, int, const char *); +extern int s390_const_double_ok_for_constraint_p (rtx, int, const char *); +extern int s390_single_part (rtx, enum machine_mode, enum machine_mode, int); +extern unsigned HOST_WIDE_INT s390_extract_part (rtx, enum machine_mode, int); +extern bool s390_contiguous_bitmask_p (unsigned HOST_WIDE_INT, int, int *, int *); +extern bool s390_split_ok_p (rtx, rtx, enum machine_mode, int); +extern bool s390_overlap_p (rtx, rtx, HOST_WIDE_INT); +extern bool s390_offset_p (rtx, rtx, rtx); +extern int tls_symbolic_operand (rtx); + +extern bool s390_match_ccmode (rtx, enum machine_mode); +extern enum machine_mode s390_tm_ccmode (rtx, rtx, bool); +extern enum machine_mode s390_select_ccmode (enum rtx_code, rtx, rtx); +extern void s390_canonicalize_comparison (enum rtx_code *, rtx *, rtx *); +extern rtx s390_emit_compare (enum rtx_code, rtx, rtx); +extern void s390_emit_jump (rtx, rtx); +extern bool symbolic_reference_mentioned_p (rtx); +extern bool tls_symbolic_reference_mentioned_p (rtx); +extern bool legitimate_la_operand_p (rtx); +extern bool preferred_la_operand_p (rtx, rtx); +extern int legitimate_pic_operand_p (rtx); +extern int legitimate_constant_p (rtx); +extern bool legitimate_reload_constant_p (rtx); +extern rtx legitimize_pic_address (rtx, rtx); +extern rtx legitimize_reload_address (rtx, enum machine_mode, int, int); +extern enum reg_class s390_secondary_input_reload_class (enum reg_class, + enum machine_mode, + rtx); +extern enum reg_class s390_secondary_output_reload_class (enum reg_class, + enum machine_mode, + rtx); +extern void s390_reload_larl_operand (rtx , rtx , rtx); +extern void s390_reload_symref_address (rtx , rtx , rtx , bool); +extern void s390_expand_plus_operand (rtx, rtx, rtx); +extern void emit_symbolic_move (rtx *); +extern void s390_load_address (rtx, rtx); +extern void s390_expand_movmem (rtx, rtx, rtx); +extern void s390_expand_setmem (rtx, rtx, rtx); +extern void s390_expand_cmpmem (rtx, rtx, rtx, rtx); +extern bool s390_expand_addcc (enum rtx_code, rtx, rtx, rtx, rtx, rtx); +extern bool s390_expand_insv (rtx, rtx, rtx, rtx); +extern void s390_expand_cs_hqi (enum machine_mode, rtx, rtx, rtx, rtx); +extern void s390_expand_atomic (enum machine_mode, enum rtx_code, + rtx, rtx, rtx, bool); +extern rtx s390_return_addr_rtx (int, rtx); +extern rtx s390_back_chain_rtx (void); +extern rtx s390_emit_call (rtx, rtx, rtx, rtx); +extern void s390_expand_logical_operator (enum rtx_code, + enum machine_mode, rtx *); +extern bool s390_logical_operator_ok_p (rtx *); +extern void s390_narrow_logical_operator (enum rtx_code, rtx *, rtx *); +extern void s390_split_access_reg (rtx, rtx *, rtx *); + +extern void print_operand_address (FILE *, rtx); +extern void print_operand (FILE *, rtx, int); +extern void s390_output_pool_entry (rtx, enum machine_mode, unsigned int); +extern int s390_label_align (rtx); +extern int s390_agen_dep_p (rtx, rtx); +extern rtx s390_load_got (void); +extern rtx s390_get_thread_pointer (void); +extern void s390_emit_tpf_eh_return (rtx); +extern bool s390_legitimate_address_without_index_p (rtx); +extern bool s390_decompose_shift_count (rtx, rtx *, HOST_WIDE_INT *); +extern int s390_branch_condition_mask (rtx); +extern int s390_compare_and_branch_condition_mask (rtx); + +#endif /* RTX_CODE */ diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c new file mode 100644 index 000000000..9b275b0ae --- /dev/null +++ b/gcc/config/s390/s390.c @@ -0,0 +1,10845 @@ +/* Subroutines used for code generation on IBM S/390 and zSeries + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, + 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. + Contributed by Hartmut Penner (hpenner@de.ibm.com) and + Ulrich Weigand (uweigand@de.ibm.com) and + Andreas Krebbel (Andreas.Krebbel@de.ibm.com). + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "rtl.h" +#include "tree.h" +#include "tm_p.h" +#include "regs.h" +#include "hard-reg-set.h" +#include "insn-config.h" +#include "conditions.h" +#include "output.h" +#include "insn-attr.h" +#include "flags.h" +#include "except.h" +#include "function.h" +#include "recog.h" +#include "expr.h" +#include "reload.h" +#include "diagnostic-core.h" +#include "basic-block.h" +#include "integrate.h" +#include "ggc.h" +#include "target.h" +#include "target-def.h" +#include "debug.h" +#include "langhooks.h" +#include "optabs.h" +#include "gimple.h" +#include "df.h" +#include "params.h" +#include "cfgloop.h" + + +/* Define the specific costs for a given cpu. */ + +struct processor_costs +{ + /* multiplication */ + const int m; /* cost of an M instruction. */ + const int mghi; /* cost of an MGHI instruction. */ + const int mh; /* cost of an MH instruction. */ + const int mhi; /* cost of an MHI instruction. */ + const int ml; /* cost of an ML instruction. */ + const int mr; /* cost of an MR instruction. */ + const int ms; /* cost of an MS instruction. */ + const int msg; /* cost of an MSG instruction. */ + const int msgf; /* cost of an MSGF instruction. */ + const int msgfr; /* cost of an MSGFR instruction. */ + const int msgr; /* cost of an MSGR instruction. */ + const int msr; /* cost of an MSR instruction. */ + const int mult_df; /* cost of multiplication in DFmode. */ + const int mxbr; + /* square root */ + const int sqxbr; /* cost of square root in TFmode. */ + const int sqdbr; /* cost of square root in DFmode. */ + const int sqebr; /* cost of square root in SFmode. */ + /* multiply and add */ + const int madbr; /* cost of multiply and add in DFmode. */ + const int maebr; /* cost of multiply and add in SFmode. */ + /* division */ + const int dxbr; + const int ddbr; + const int debr; + const int dlgr; + const int dlr; + const int dr; + const int dsgfr; + const int dsgr; +}; + +const struct processor_costs *s390_cost; + +static const +struct processor_costs z900_cost = +{ + COSTS_N_INSNS (5), /* M */ + COSTS_N_INSNS (10), /* MGHI */ + COSTS_N_INSNS (5), /* MH */ + COSTS_N_INSNS (4), /* MHI */ + COSTS_N_INSNS (5), /* ML */ + COSTS_N_INSNS (5), /* MR */ + COSTS_N_INSNS (4), /* MS */ + COSTS_N_INSNS (15), /* MSG */ + COSTS_N_INSNS (7), /* MSGF */ + COSTS_N_INSNS (7), /* MSGFR */ + COSTS_N_INSNS (10), /* MSGR */ + COSTS_N_INSNS (4), /* MSR */ + COSTS_N_INSNS (7), /* multiplication in DFmode */ + COSTS_N_INSNS (13), /* MXBR */ + COSTS_N_INSNS (136), /* SQXBR */ + COSTS_N_INSNS (44), /* SQDBR */ + COSTS_N_INSNS (35), /* SQEBR */ + COSTS_N_INSNS (18), /* MADBR */ + COSTS_N_INSNS (13), /* MAEBR */ + COSTS_N_INSNS (134), /* DXBR */ + COSTS_N_INSNS (30), /* DDBR */ + COSTS_N_INSNS (27), /* DEBR */ + COSTS_N_INSNS (220), /* DLGR */ + COSTS_N_INSNS (34), /* DLR */ + COSTS_N_INSNS (34), /* DR */ + COSTS_N_INSNS (32), /* DSGFR */ + COSTS_N_INSNS (32), /* DSGR */ +}; + +static const +struct processor_costs z990_cost = +{ + COSTS_N_INSNS (4), /* M */ + COSTS_N_INSNS (2), /* MGHI */ + COSTS_N_INSNS (2), /* MH */ + COSTS_N_INSNS (2), /* MHI */ + COSTS_N_INSNS (4), /* ML */ + COSTS_N_INSNS (4), /* MR */ + COSTS_N_INSNS (5), /* MS */ + COSTS_N_INSNS (6), /* MSG */ + COSTS_N_INSNS (4), /* MSGF */ + COSTS_N_INSNS (4), /* MSGFR */ + COSTS_N_INSNS (4), /* MSGR */ + COSTS_N_INSNS (4), /* MSR */ + COSTS_N_INSNS (1), /* multiplication in DFmode */ + COSTS_N_INSNS (28), /* MXBR */ + COSTS_N_INSNS (130), /* SQXBR */ + COSTS_N_INSNS (66), /* SQDBR */ + COSTS_N_INSNS (38), /* SQEBR */ + COSTS_N_INSNS (1), /* MADBR */ + COSTS_N_INSNS (1), /* MAEBR */ + COSTS_N_INSNS (60), /* DXBR */ + COSTS_N_INSNS (40), /* DDBR */ + COSTS_N_INSNS (26), /* DEBR */ + COSTS_N_INSNS (176), /* DLGR */ + COSTS_N_INSNS (31), /* DLR */ + COSTS_N_INSNS (31), /* DR */ + COSTS_N_INSNS (31), /* DSGFR */ + COSTS_N_INSNS (31), /* DSGR */ +}; + +static const +struct processor_costs z9_109_cost = +{ + COSTS_N_INSNS (4), /* M */ + COSTS_N_INSNS (2), /* MGHI */ + COSTS_N_INSNS (2), /* MH */ + COSTS_N_INSNS (2), /* MHI */ + COSTS_N_INSNS (4), /* ML */ + COSTS_N_INSNS (4), /* MR */ + COSTS_N_INSNS (5), /* MS */ + COSTS_N_INSNS (6), /* MSG */ + COSTS_N_INSNS (4), /* MSGF */ + COSTS_N_INSNS (4), /* MSGFR */ + COSTS_N_INSNS (4), /* MSGR */ + COSTS_N_INSNS (4), /* MSR */ + COSTS_N_INSNS (1), /* multiplication in DFmode */ + COSTS_N_INSNS (28), /* MXBR */ + COSTS_N_INSNS (130), /* SQXBR */ + COSTS_N_INSNS (66), /* SQDBR */ + COSTS_N_INSNS (38), /* SQEBR */ + COSTS_N_INSNS (1), /* MADBR */ + COSTS_N_INSNS (1), /* MAEBR */ + COSTS_N_INSNS (60), /* DXBR */ + COSTS_N_INSNS (40), /* DDBR */ + COSTS_N_INSNS (26), /* DEBR */ + COSTS_N_INSNS (30), /* DLGR */ + COSTS_N_INSNS (23), /* DLR */ + COSTS_N_INSNS (23), /* DR */ + COSTS_N_INSNS (24), /* DSGFR */ + COSTS_N_INSNS (24), /* DSGR */ +}; + +static const +struct processor_costs z10_cost = +{ + COSTS_N_INSNS (10), /* M */ + COSTS_N_INSNS (10), /* MGHI */ + COSTS_N_INSNS (10), /* MH */ + COSTS_N_INSNS (10), /* MHI */ + COSTS_N_INSNS (10), /* ML */ + COSTS_N_INSNS (10), /* MR */ + COSTS_N_INSNS (10), /* MS */ + COSTS_N_INSNS (10), /* MSG */ + COSTS_N_INSNS (10), /* MSGF */ + COSTS_N_INSNS (10), /* MSGFR */ + COSTS_N_INSNS (10), /* MSGR */ + COSTS_N_INSNS (10), /* MSR */ + COSTS_N_INSNS (1) , /* multiplication in DFmode */ + COSTS_N_INSNS (50), /* MXBR */ + COSTS_N_INSNS (120), /* SQXBR */ + COSTS_N_INSNS (52), /* SQDBR */ + COSTS_N_INSNS (38), /* SQEBR */ + COSTS_N_INSNS (1), /* MADBR */ + COSTS_N_INSNS (1), /* MAEBR */ + COSTS_N_INSNS (111), /* DXBR */ + COSTS_N_INSNS (39), /* DDBR */ + COSTS_N_INSNS (32), /* DEBR */ + COSTS_N_INSNS (160), /* DLGR */ + COSTS_N_INSNS (71), /* DLR */ + COSTS_N_INSNS (71), /* DR */ + COSTS_N_INSNS (71), /* DSGFR */ + COSTS_N_INSNS (71), /* DSGR */ +}; + +static const +struct processor_costs z196_cost = +{ + COSTS_N_INSNS (7), /* M */ + COSTS_N_INSNS (5), /* MGHI */ + COSTS_N_INSNS (5), /* MH */ + COSTS_N_INSNS (5), /* MHI */ + COSTS_N_INSNS (7), /* ML */ + COSTS_N_INSNS (7), /* MR */ + COSTS_N_INSNS (6), /* MS */ + COSTS_N_INSNS (8), /* MSG */ + COSTS_N_INSNS (6), /* MSGF */ + COSTS_N_INSNS (6), /* MSGFR */ + COSTS_N_INSNS (8), /* MSGR */ + COSTS_N_INSNS (6), /* MSR */ + COSTS_N_INSNS (1) , /* multiplication in DFmode */ + COSTS_N_INSNS (40), /* MXBR B+40 */ + COSTS_N_INSNS (100), /* SQXBR B+100 */ + COSTS_N_INSNS (42), /* SQDBR B+42 */ + COSTS_N_INSNS (28), /* SQEBR B+28 */ + COSTS_N_INSNS (1), /* MADBR B */ + COSTS_N_INSNS (1), /* MAEBR B */ + COSTS_N_INSNS (101), /* DXBR B+101 */ + COSTS_N_INSNS (29), /* DDBR */ + COSTS_N_INSNS (22), /* DEBR */ + COSTS_N_INSNS (160), /* DLGR cracked */ + COSTS_N_INSNS (160), /* DLR cracked */ + COSTS_N_INSNS (160), /* DR expanded */ + COSTS_N_INSNS (160), /* DSGFR cracked */ + COSTS_N_INSNS (160), /* DSGR cracked */ +}; + +extern int reload_completed; + +/* Kept up to date using the SCHED_VARIABLE_ISSUE hook. */ +static rtx last_scheduled_insn; + +/* Structure used to hold the components of a S/390 memory + address. A legitimate address on S/390 is of the general + form + base + index + displacement + where any of the components is optional. + + base and index are registers of the class ADDR_REGS, + displacement is an unsigned 12-bit immediate constant. */ + +struct s390_address +{ + rtx base; + rtx indx; + rtx disp; + bool pointer; + bool literal_pool; +}; + +/* Which cpu are we tuning for. */ +enum processor_type s390_tune = PROCESSOR_max; +int s390_tune_flags; +/* Which instruction set architecture to use. */ +enum processor_type s390_arch; +int s390_arch_flags; + +HOST_WIDE_INT s390_warn_framesize = 0; +HOST_WIDE_INT s390_stack_size = 0; +HOST_WIDE_INT s390_stack_guard = 0; + +/* The following structure is embedded in the machine + specific part of struct function. */ + +struct GTY (()) s390_frame_layout +{ + /* Offset within stack frame. */ + HOST_WIDE_INT gprs_offset; + HOST_WIDE_INT f0_offset; + HOST_WIDE_INT f4_offset; + HOST_WIDE_INT f8_offset; + HOST_WIDE_INT backchain_offset; + + /* Number of first and last gpr where slots in the register + save area are reserved for. */ + int first_save_gpr_slot; + int last_save_gpr_slot; + + /* Number of first and last gpr to be saved, restored. */ + int first_save_gpr; + int first_restore_gpr; + int last_save_gpr; + int last_restore_gpr; + + /* Bits standing for floating point registers. Set, if the + respective register has to be saved. Starting with reg 16 (f0) + at the rightmost bit. + Bit 15 - 8 7 6 5 4 3 2 1 0 + fpr 15 - 8 7 5 3 1 6 4 2 0 + reg 31 - 24 23 22 21 20 19 18 17 16 */ + unsigned int fpr_bitmap; + + /* Number of floating point registers f8-f15 which must be saved. */ + int high_fprs; + + /* Set if return address needs to be saved. + This flag is set by s390_return_addr_rtx if it could not use + the initial value of r14 and therefore depends on r14 saved + to the stack. */ + bool save_return_addr_p; + + /* Size of stack frame. */ + HOST_WIDE_INT frame_size; +}; + +/* Define the structure for the machine field in struct function. */ + +struct GTY(()) machine_function +{ + struct s390_frame_layout frame_layout; + + /* Literal pool base register. */ + rtx base_reg; + + /* True if we may need to perform branch splitting. */ + bool split_branches_pending_p; + + /* Some local-dynamic TLS symbol name. */ + const char *some_ld_name; + + bool has_landing_pad_p; +}; + +/* Few accessor macros for struct cfun->machine->s390_frame_layout. */ + +#define cfun_frame_layout (cfun->machine->frame_layout) +#define cfun_save_high_fprs_p (!!cfun_frame_layout.high_fprs) +#define cfun_gprs_save_area_size ((cfun_frame_layout.last_save_gpr_slot - \ + cfun_frame_layout.first_save_gpr_slot + 1) * UNITS_PER_LONG) +#define cfun_set_fpr_bit(BITNUM) (cfun->machine->frame_layout.fpr_bitmap |= \ + (1 << (BITNUM))) +#define cfun_fpr_bit_p(BITNUM) (!!(cfun->machine->frame_layout.fpr_bitmap & \ + (1 << (BITNUM)))) + +/* Number of GPRs and FPRs used for argument passing. */ +#define GP_ARG_NUM_REG 5 +#define FP_ARG_NUM_REG (TARGET_64BIT? 4 : 2) + +/* A couple of shortcuts. */ +#define CONST_OK_FOR_J(x) \ + CONST_OK_FOR_CONSTRAINT_P((x), 'J', "J") +#define CONST_OK_FOR_K(x) \ + CONST_OK_FOR_CONSTRAINT_P((x), 'K', "K") +#define CONST_OK_FOR_Os(x) \ + CONST_OK_FOR_CONSTRAINT_P((x), 'O', "Os") +#define CONST_OK_FOR_Op(x) \ + CONST_OK_FOR_CONSTRAINT_P((x), 'O', "Op") +#define CONST_OK_FOR_On(x) \ + CONST_OK_FOR_CONSTRAINT_P((x), 'O', "On") + +#define REGNO_PAIR_OK(REGNO, MODE) \ + (HARD_REGNO_NREGS ((REGNO), (MODE)) == 1 || !((REGNO) & 1)) + +/* That's the read ahead of the dynamic branch prediction unit in + bytes on a z10 (or higher) CPU. */ +#define PREDICT_DISTANCE (TARGET_Z10 ? 384 : 2048) + +/* Return the alignment for LABEL. We default to the -falign-labels + value except for the literal pool base label. */ +int +s390_label_align (rtx label) +{ + rtx prev_insn = prev_active_insn (label); + + if (prev_insn == NULL_RTX) + goto old; + + prev_insn = single_set (prev_insn); + + if (prev_insn == NULL_RTX) + goto old; + + prev_insn = SET_SRC (prev_insn); + + /* Don't align literal pool base labels. */ + if (GET_CODE (prev_insn) == UNSPEC + && XINT (prev_insn, 1) == UNSPEC_MAIN_BASE) + return 0; + + old: + return align_labels_log; +} + +static enum machine_mode +s390_libgcc_cmp_return_mode (void) +{ + return TARGET_64BIT ? DImode : SImode; +} + +static enum machine_mode +s390_libgcc_shift_count_mode (void) +{ + return TARGET_64BIT ? DImode : SImode; +} + +static enum machine_mode +s390_unwind_word_mode (void) +{ + return TARGET_64BIT ? DImode : SImode; +} + +/* Return true if the back end supports mode MODE. */ +static bool +s390_scalar_mode_supported_p (enum machine_mode mode) +{ + /* In contrast to the default implementation reject TImode constants on 31bit + TARGET_ZARCH for ABI compliance. */ + if (!TARGET_64BIT && TARGET_ZARCH && mode == TImode) + return false; + + if (DECIMAL_FLOAT_MODE_P (mode)) + return default_decimal_float_supported_p (); + + return default_scalar_mode_supported_p (mode); +} + +/* Set the has_landing_pad_p flag in struct machine_function to VALUE. */ + +void +s390_set_has_landing_pad_p (bool value) +{ + cfun->machine->has_landing_pad_p = value; +} + +/* If two condition code modes are compatible, return a condition code + mode which is compatible with both. Otherwise, return + VOIDmode. */ + +static enum machine_mode +s390_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2) +{ + if (m1 == m2) + return m1; + + switch (m1) + { + case CCZmode: + if (m2 == CCUmode || m2 == CCTmode || m2 == CCZ1mode + || m2 == CCSmode || m2 == CCSRmode || m2 == CCURmode) + return m2; + return VOIDmode; + + case CCSmode: + case CCUmode: + case CCTmode: + case CCSRmode: + case CCURmode: + case CCZ1mode: + if (m2 == CCZmode) + return m1; + + return VOIDmode; + + default: + return VOIDmode; + } + return VOIDmode; +} + +/* Return true if SET either doesn't set the CC register, or else + the source and destination have matching CC modes and that + CC mode is at least as constrained as REQ_MODE. */ + +static bool +s390_match_ccmode_set (rtx set, enum machine_mode req_mode) +{ + enum machine_mode set_mode; + + gcc_assert (GET_CODE (set) == SET); + + if (GET_CODE (SET_DEST (set)) != REG || !CC_REGNO_P (REGNO (SET_DEST (set)))) + return 1; + + set_mode = GET_MODE (SET_DEST (set)); + switch (set_mode) + { + case CCSmode: + case CCSRmode: + case CCUmode: + case CCURmode: + case CCLmode: + case CCL1mode: + case CCL2mode: + case CCL3mode: + case CCT1mode: + case CCT2mode: + case CCT3mode: + if (req_mode != set_mode) + return 0; + break; + + case CCZmode: + if (req_mode != CCSmode && req_mode != CCUmode && req_mode != CCTmode + && req_mode != CCSRmode && req_mode != CCURmode) + return 0; + break; + + case CCAPmode: + case CCANmode: + if (req_mode != CCAmode) + return 0; + break; + + default: + gcc_unreachable (); + } + + return (GET_MODE (SET_SRC (set)) == set_mode); +} + +/* Return true if every SET in INSN that sets the CC register + has source and destination with matching CC modes and that + CC mode is at least as constrained as REQ_MODE. + If REQ_MODE is VOIDmode, always return false. */ + +bool +s390_match_ccmode (rtx insn, enum machine_mode req_mode) +{ + int i; + + /* s390_tm_ccmode returns VOIDmode to indicate failure. */ + if (req_mode == VOIDmode) + return false; + + if (GET_CODE (PATTERN (insn)) == SET) + return s390_match_ccmode_set (PATTERN (insn), req_mode); + + if (GET_CODE (PATTERN (insn)) == PARALLEL) + for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) + { + rtx set = XVECEXP (PATTERN (insn), 0, i); + if (GET_CODE (set) == SET) + if (!s390_match_ccmode_set (set, req_mode)) + return false; + } + + return true; +} + +/* If a test-under-mask instruction can be used to implement + (compare (and ... OP1) OP2), return the CC mode required + to do that. Otherwise, return VOIDmode. + MIXED is true if the instruction can distinguish between + CC1 and CC2 for mixed selected bits (TMxx), it is false + if the instruction cannot (TM). */ + +enum machine_mode +s390_tm_ccmode (rtx op1, rtx op2, bool mixed) +{ + int bit0, bit1; + + /* ??? Fixme: should work on CONST_DOUBLE as well. */ + if (GET_CODE (op1) != CONST_INT || GET_CODE (op2) != CONST_INT) + return VOIDmode; + + /* Selected bits all zero: CC0. + e.g.: int a; if ((a & (16 + 128)) == 0) */ + if (INTVAL (op2) == 0) + return CCTmode; + + /* Selected bits all one: CC3. + e.g.: int a; if ((a & (16 + 128)) == 16 + 128) */ + if (INTVAL (op2) == INTVAL (op1)) + return CCT3mode; + + /* Exactly two bits selected, mixed zeroes and ones: CC1 or CC2. e.g.: + int a; + if ((a & (16 + 128)) == 16) -> CCT1 + if ((a & (16 + 128)) == 128) -> CCT2 */ + if (mixed) + { + bit1 = exact_log2 (INTVAL (op2)); + bit0 = exact_log2 (INTVAL (op1) ^ INTVAL (op2)); + if (bit0 != -1 && bit1 != -1) + return bit0 > bit1 ? CCT1mode : CCT2mode; + } + + return VOIDmode; +} + +/* Given a comparison code OP (EQ, NE, etc.) and the operands + OP0 and OP1 of a COMPARE, return the mode to be used for the + comparison. */ + +enum machine_mode +s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1) +{ + switch (code) + { + case EQ: + case NE: + if ((GET_CODE (op0) == NEG || GET_CODE (op0) == ABS) + && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT) + return CCAPmode; + if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT + && CONST_OK_FOR_K (INTVAL (XEXP (op0, 1)))) + return CCAPmode; + if ((GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS + || GET_CODE (op1) == NEG) + && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT) + return CCLmode; + + if (GET_CODE (op0) == AND) + { + /* Check whether we can potentially do it via TM. */ + enum machine_mode ccmode; + ccmode = s390_tm_ccmode (XEXP (op0, 1), op1, 1); + if (ccmode != VOIDmode) + { + /* Relax CCTmode to CCZmode to allow fall-back to AND + if that turns out to be beneficial. */ + return ccmode == CCTmode ? CCZmode : ccmode; + } + } + + if (register_operand (op0, HImode) + && GET_CODE (op1) == CONST_INT + && (INTVAL (op1) == -1 || INTVAL (op1) == 65535)) + return CCT3mode; + if (register_operand (op0, QImode) + && GET_CODE (op1) == CONST_INT + && (INTVAL (op1) == -1 || INTVAL (op1) == 255)) + return CCT3mode; + + return CCZmode; + + case LE: + case LT: + case GE: + case GT: + /* The only overflow condition of NEG and ABS happens when + -INT_MAX is used as parameter, which stays negative. So + we have an overflow from a positive value to a negative. + Using CCAP mode the resulting cc can be used for comparisons. */ + if ((GET_CODE (op0) == NEG || GET_CODE (op0) == ABS) + && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT) + return CCAPmode; + + /* If constants are involved in an add instruction it is possible to use + the resulting cc for comparisons with zero. Knowing the sign of the + constant the overflow behavior gets predictable. e.g.: + int a, b; if ((b = a + c) > 0) + with c as a constant value: c < 0 -> CCAN and c >= 0 -> CCAP */ + if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT + && CONST_OK_FOR_K (INTVAL (XEXP (op0, 1)))) + { + if (INTVAL (XEXP((op0), 1)) < 0) + return CCANmode; + else + return CCAPmode; + } + /* Fall through. */ + case UNORDERED: + case ORDERED: + case UNEQ: + case UNLE: + case UNLT: + case UNGE: + case UNGT: + case LTGT: + if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND) + && GET_CODE (op1) != CONST_INT) + return CCSRmode; + return CCSmode; + + case LTU: + case GEU: + if (GET_CODE (op0) == PLUS + && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT) + return CCL1mode; + + if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND) + && GET_CODE (op1) != CONST_INT) + return CCURmode; + return CCUmode; + + case LEU: + case GTU: + if (GET_CODE (op0) == MINUS + && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT) + return CCL2mode; + + if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND) + && GET_CODE (op1) != CONST_INT) + return CCURmode; + return CCUmode; + + default: + gcc_unreachable (); + } +} + +/* Replace the comparison OP0 CODE OP1 by a semantically equivalent one + that we can implement more efficiently. */ + +void +s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1) +{ + /* Convert ZERO_EXTRACT back to AND to enable TM patterns. */ + if ((*code == EQ || *code == NE) + && *op1 == const0_rtx + && GET_CODE (*op0) == ZERO_EXTRACT + && GET_CODE (XEXP (*op0, 1)) == CONST_INT + && GET_CODE (XEXP (*op0, 2)) == CONST_INT + && SCALAR_INT_MODE_P (GET_MODE (XEXP (*op0, 0)))) + { + rtx inner = XEXP (*op0, 0); + HOST_WIDE_INT modesize = GET_MODE_BITSIZE (GET_MODE (inner)); + HOST_WIDE_INT len = INTVAL (XEXP (*op0, 1)); + HOST_WIDE_INT pos = INTVAL (XEXP (*op0, 2)); + + if (len > 0 && len < modesize + && pos >= 0 && pos + len <= modesize + && modesize <= HOST_BITS_PER_WIDE_INT) + { + unsigned HOST_WIDE_INT block; + block = ((unsigned HOST_WIDE_INT) 1 << len) - 1; + block <<= modesize - pos - len; + + *op0 = gen_rtx_AND (GET_MODE (inner), inner, + gen_int_mode (block, GET_MODE (inner))); + } + } + + /* Narrow AND of memory against immediate to enable TM. */ + if ((*code == EQ || *code == NE) + && *op1 == const0_rtx + && GET_CODE (*op0) == AND + && GET_CODE (XEXP (*op0, 1)) == CONST_INT + && SCALAR_INT_MODE_P (GET_MODE (XEXP (*op0, 0)))) + { + rtx inner = XEXP (*op0, 0); + rtx mask = XEXP (*op0, 1); + + /* Ignore paradoxical SUBREGs if all extra bits are masked out. */ + if (GET_CODE (inner) == SUBREG + && SCALAR_INT_MODE_P (GET_MODE (SUBREG_REG (inner))) + && (GET_MODE_SIZE (GET_MODE (inner)) + >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner)))) + && ((INTVAL (mask) + & GET_MODE_MASK (GET_MODE (inner)) + & ~GET_MODE_MASK (GET_MODE (SUBREG_REG (inner)))) + == 0)) + inner = SUBREG_REG (inner); + + /* Do not change volatile MEMs. */ + if (MEM_P (inner) && !MEM_VOLATILE_P (inner)) + { + int part = s390_single_part (XEXP (*op0, 1), + GET_MODE (inner), QImode, 0); + if (part >= 0) + { + mask = gen_int_mode (s390_extract_part (mask, QImode, 0), QImode); + inner = adjust_address_nv (inner, QImode, part); + *op0 = gen_rtx_AND (QImode, inner, mask); + } + } + } + + /* Narrow comparisons against 0xffff to HImode if possible. */ + if ((*code == EQ || *code == NE) + && GET_CODE (*op1) == CONST_INT + && INTVAL (*op1) == 0xffff + && SCALAR_INT_MODE_P (GET_MODE (*op0)) + && (nonzero_bits (*op0, GET_MODE (*op0)) + & ~(unsigned HOST_WIDE_INT) 0xffff) == 0) + { + *op0 = gen_lowpart (HImode, *op0); + *op1 = constm1_rtx; + } + + /* Remove redundant UNSPEC_CCU_TO_INT conversions if possible. */ + if (GET_CODE (*op0) == UNSPEC + && XINT (*op0, 1) == UNSPEC_CCU_TO_INT + && XVECLEN (*op0, 0) == 1 + && GET_MODE (XVECEXP (*op0, 0, 0)) == CCUmode + && GET_CODE (XVECEXP (*op0, 0, 0)) == REG + && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM + && *op1 == const0_rtx) + { + enum rtx_code new_code = UNKNOWN; + switch (*code) + { + case EQ: new_code = EQ; break; + case NE: new_code = NE; break; + case LT: new_code = GTU; break; + case GT: new_code = LTU; break; + case LE: new_code = GEU; break; + case GE: new_code = LEU; break; + default: break; + } + + if (new_code != UNKNOWN) + { + *op0 = XVECEXP (*op0, 0, 0); + *code = new_code; + } + } + + /* Remove redundant UNSPEC_CCZ_TO_INT conversions if possible. */ + if (GET_CODE (*op0) == UNSPEC + && XINT (*op0, 1) == UNSPEC_CCZ_TO_INT + && XVECLEN (*op0, 0) == 1 + && GET_MODE (XVECEXP (*op0, 0, 0)) == CCZmode + && GET_CODE (XVECEXP (*op0, 0, 0)) == REG + && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM + && *op1 == const0_rtx) + { + enum rtx_code new_code = UNKNOWN; + switch (*code) + { + case EQ: new_code = EQ; break; + case NE: new_code = NE; break; + default: break; + } + + if (new_code != UNKNOWN) + { + *op0 = XVECEXP (*op0, 0, 0); + *code = new_code; + } + } + + /* Simplify cascaded EQ, NE with const0_rtx. */ + if ((*code == NE || *code == EQ) + && (GET_CODE (*op0) == EQ || GET_CODE (*op0) == NE) + && GET_MODE (*op0) == SImode + && GET_MODE (XEXP (*op0, 0)) == CCZ1mode + && REG_P (XEXP (*op0, 0)) + && XEXP (*op0, 1) == const0_rtx + && *op1 == const0_rtx) + { + if ((*code == EQ && GET_CODE (*op0) == NE) + || (*code == NE && GET_CODE (*op0) == EQ)) + *code = EQ; + else + *code = NE; + *op0 = XEXP (*op0, 0); + } + + /* Prefer register over memory as first operand. */ + if (MEM_P (*op0) && REG_P (*op1)) + { + rtx tem = *op0; *op0 = *op1; *op1 = tem; + *code = swap_condition (*code); + } +} + +/* Emit a compare instruction suitable to implement the comparison + OP0 CODE OP1. Return the correct condition RTL to be placed in + the IF_THEN_ELSE of the conditional branch testing the result. */ + +rtx +s390_emit_compare (enum rtx_code code, rtx op0, rtx op1) +{ + enum machine_mode mode = s390_select_ccmode (code, op0, op1); + rtx cc; + + /* Do not output a redundant compare instruction if a compare_and_swap + pattern already computed the result and the machine modes are compatible. */ + if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC) + { + gcc_assert (s390_cc_modes_compatible (GET_MODE (op0), mode) + == GET_MODE (op0)); + cc = op0; + } + else + { + cc = gen_rtx_REG (mode, CC_REGNUM); + emit_insn (gen_rtx_SET (VOIDmode, cc, gen_rtx_COMPARE (mode, op0, op1))); + } + + return gen_rtx_fmt_ee (code, VOIDmode, cc, const0_rtx); +} + +/* Emit a SImode compare and swap instruction setting MEM to NEW_RTX if OLD + matches CMP. + Return the correct condition RTL to be placed in the IF_THEN_ELSE of the + conditional branch testing the result. */ + +static rtx +s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem, rtx cmp, rtx new_rtx) +{ + emit_insn (gen_sync_compare_and_swapsi (old, mem, cmp, new_rtx)); + return s390_emit_compare (code, gen_rtx_REG (CCZ1mode, CC_REGNUM), const0_rtx); +} + +/* Emit a jump instruction to TARGET. If COND is NULL_RTX, emit an + unconditional jump, else a conditional jump under condition COND. */ + +void +s390_emit_jump (rtx target, rtx cond) +{ + rtx insn; + + target = gen_rtx_LABEL_REF (VOIDmode, target); + if (cond) + target = gen_rtx_IF_THEN_ELSE (VOIDmode, cond, target, pc_rtx); + + insn = gen_rtx_SET (VOIDmode, pc_rtx, target); + emit_jump_insn (insn); +} + +/* Return branch condition mask to implement a branch + specified by CODE. Return -1 for invalid comparisons. */ + +int +s390_branch_condition_mask (rtx code) +{ + const int CC0 = 1 << 3; + const int CC1 = 1 << 2; + const int CC2 = 1 << 1; + const int CC3 = 1 << 0; + + gcc_assert (GET_CODE (XEXP (code, 0)) == REG); + gcc_assert (REGNO (XEXP (code, 0)) == CC_REGNUM); + gcc_assert (XEXP (code, 1) == const0_rtx); + + switch (GET_MODE (XEXP (code, 0))) + { + case CCZmode: + case CCZ1mode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC1 | CC2 | CC3; + default: return -1; + } + break; + + case CCT1mode: + switch (GET_CODE (code)) + { + case EQ: return CC1; + case NE: return CC0 | CC2 | CC3; + default: return -1; + } + break; + + case CCT2mode: + switch (GET_CODE (code)) + { + case EQ: return CC2; + case NE: return CC0 | CC1 | CC3; + default: return -1; + } + break; + + case CCT3mode: + switch (GET_CODE (code)) + { + case EQ: return CC3; + case NE: return CC0 | CC1 | CC2; + default: return -1; + } + break; + + case CCLmode: + switch (GET_CODE (code)) + { + case EQ: return CC0 | CC2; + case NE: return CC1 | CC3; + default: return -1; + } + break; + + case CCL1mode: + switch (GET_CODE (code)) + { + case LTU: return CC2 | CC3; /* carry */ + case GEU: return CC0 | CC1; /* no carry */ + default: return -1; + } + break; + + case CCL2mode: + switch (GET_CODE (code)) + { + case GTU: return CC0 | CC1; /* borrow */ + case LEU: return CC2 | CC3; /* no borrow */ + default: return -1; + } + break; + + case CCL3mode: + switch (GET_CODE (code)) + { + case EQ: return CC0 | CC2; + case NE: return CC1 | CC3; + case LTU: return CC1; + case GTU: return CC3; + case LEU: return CC1 | CC2; + case GEU: return CC2 | CC3; + default: return -1; + } + + case CCUmode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC1 | CC2 | CC3; + case LTU: return CC1; + case GTU: return CC2; + case LEU: return CC0 | CC1; + case GEU: return CC0 | CC2; + default: return -1; + } + break; + + case CCURmode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC2 | CC1 | CC3; + case LTU: return CC2; + case GTU: return CC1; + case LEU: return CC0 | CC2; + case GEU: return CC0 | CC1; + default: return -1; + } + break; + + case CCAPmode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC1 | CC2 | CC3; + case LT: return CC1 | CC3; + case GT: return CC2; + case LE: return CC0 | CC1 | CC3; + case GE: return CC0 | CC2; + default: return -1; + } + break; + + case CCANmode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC1 | CC2 | CC3; + case LT: return CC1; + case GT: return CC2 | CC3; + case LE: return CC0 | CC1; + case GE: return CC0 | CC2 | CC3; + default: return -1; + } + break; + + case CCSmode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC1 | CC2 | CC3; + case LT: return CC1; + case GT: return CC2; + case LE: return CC0 | CC1; + case GE: return CC0 | CC2; + case UNORDERED: return CC3; + case ORDERED: return CC0 | CC1 | CC2; + case UNEQ: return CC0 | CC3; + case UNLT: return CC1 | CC3; + case UNGT: return CC2 | CC3; + case UNLE: return CC0 | CC1 | CC3; + case UNGE: return CC0 | CC2 | CC3; + case LTGT: return CC1 | CC2; + default: return -1; + } + break; + + case CCSRmode: + switch (GET_CODE (code)) + { + case EQ: return CC0; + case NE: return CC2 | CC1 | CC3; + case LT: return CC2; + case GT: return CC1; + case LE: return CC0 | CC2; + case GE: return CC0 | CC1; + case UNORDERED: return CC3; + case ORDERED: return CC0 | CC2 | CC1; + case UNEQ: return CC0 | CC3; + case UNLT: return CC2 | CC3; + case UNGT: return CC1 | CC3; + case UNLE: return CC0 | CC2 | CC3; + case UNGE: return CC0 | CC1 | CC3; + case LTGT: return CC2 | CC1; + default: return -1; + } + break; + + default: + return -1; + } +} + + +/* Return branch condition mask to implement a compare and branch + specified by CODE. Return -1 for invalid comparisons. */ + +int +s390_compare_and_branch_condition_mask (rtx code) +{ + const int CC0 = 1 << 3; + const int CC1 = 1 << 2; + const int CC2 = 1 << 1; + + switch (GET_CODE (code)) + { + case EQ: + return CC0; + case NE: + return CC1 | CC2; + case LT: + case LTU: + return CC1; + case GT: + case GTU: + return CC2; + case LE: + case LEU: + return CC0 | CC1; + case GE: + case GEU: + return CC0 | CC2; + default: + gcc_unreachable (); + } + return -1; +} + +/* If INV is false, return assembler mnemonic string to implement + a branch specified by CODE. If INV is true, return mnemonic + for the corresponding inverted branch. */ + +static const char * +s390_branch_condition_mnemonic (rtx code, int inv) +{ + int mask; + + static const char *const mnemonic[16] = + { + NULL, "o", "h", "nle", + "l", "nhe", "lh", "ne", + "e", "nlh", "he", "nl", + "le", "nh", "no", NULL + }; + + if (GET_CODE (XEXP (code, 0)) == REG + && REGNO (XEXP (code, 0)) == CC_REGNUM + && XEXP (code, 1) == const0_rtx) + mask = s390_branch_condition_mask (code); + else + mask = s390_compare_and_branch_condition_mask (code); + + gcc_assert (mask >= 0); + + if (inv) + mask ^= 15; + + gcc_assert (mask >= 1 && mask <= 14); + + return mnemonic[mask]; +} + +/* Return the part of op which has a value different from def. + The size of the part is determined by mode. + Use this function only if you already know that op really + contains such a part. */ + +unsigned HOST_WIDE_INT +s390_extract_part (rtx op, enum machine_mode mode, int def) +{ + unsigned HOST_WIDE_INT value = 0; + int max_parts = HOST_BITS_PER_WIDE_INT / GET_MODE_BITSIZE (mode); + int part_bits = GET_MODE_BITSIZE (mode); + unsigned HOST_WIDE_INT part_mask + = ((unsigned HOST_WIDE_INT)1 << part_bits) - 1; + int i; + + for (i = 0; i < max_parts; i++) + { + if (i == 0) + value = (unsigned HOST_WIDE_INT) INTVAL (op); + else + value >>= part_bits; + + if ((value & part_mask) != (def & part_mask)) + return value & part_mask; + } + + gcc_unreachable (); +} + +/* If OP is an integer constant of mode MODE with exactly one + part of mode PART_MODE unequal to DEF, return the number of that + part. Otherwise, return -1. */ + +int +s390_single_part (rtx op, + enum machine_mode mode, + enum machine_mode part_mode, + int def) +{ + unsigned HOST_WIDE_INT value = 0; + int n_parts = GET_MODE_SIZE (mode) / GET_MODE_SIZE (part_mode); + unsigned HOST_WIDE_INT part_mask + = ((unsigned HOST_WIDE_INT)1 << GET_MODE_BITSIZE (part_mode)) - 1; + int i, part = -1; + + if (GET_CODE (op) != CONST_INT) + return -1; + + for (i = 0; i < n_parts; i++) + { + if (i == 0) + value = (unsigned HOST_WIDE_INT) INTVAL (op); + else + value >>= GET_MODE_BITSIZE (part_mode); + + if ((value & part_mask) != (def & part_mask)) + { + if (part != -1) + return -1; + else + part = i; + } + } + return part == -1 ? -1 : n_parts - 1 - part; +} + +/* Return true if IN contains a contiguous bitfield in the lower SIZE + bits and no other bits are set in IN. POS and LENGTH can be used + to obtain the start position and the length of the bitfield. + + POS gives the position of the first bit of the bitfield counting + from the lowest order bit starting with zero. In order to use this + value for S/390 instructions this has to be converted to "bits big + endian" style. */ + +bool +s390_contiguous_bitmask_p (unsigned HOST_WIDE_INT in, int size, + int *pos, int *length) +{ + int tmp_pos = 0; + int tmp_length = 0; + int i; + unsigned HOST_WIDE_INT mask = 1ULL; + bool contiguous = false; + + for (i = 0; i < size; mask <<= 1, i++) + { + if (contiguous) + { + if (mask & in) + tmp_length++; + else + break; + } + else + { + if (mask & in) + { + contiguous = true; + tmp_length++; + } + else + tmp_pos++; + } + } + + if (!tmp_length) + return false; + + /* Calculate a mask for all bits beyond the contiguous bits. */ + mask = (-1LL & ~(((1ULL << (tmp_length + tmp_pos - 1)) << 1) - 1)); + + if (mask & in) + return false; + + if (tmp_length + tmp_pos - 1 > size) + return false; + + if (length) + *length = tmp_length; + + if (pos) + *pos = tmp_pos; + + return true; +} + +/* Check whether we can (and want to) split a double-word + move in mode MODE from SRC to DST into two single-word + moves, moving the subword FIRST_SUBWORD first. */ + +bool +s390_split_ok_p (rtx dst, rtx src, enum machine_mode mode, int first_subword) +{ + /* Floating point registers cannot be split. */ + if (FP_REG_P (src) || FP_REG_P (dst)) + return false; + + /* We don't need to split if operands are directly accessible. */ + if (s_operand (src, mode) || s_operand (dst, mode)) + return false; + + /* Non-offsettable memory references cannot be split. */ + if ((GET_CODE (src) == MEM && !offsettable_memref_p (src)) + || (GET_CODE (dst) == MEM && !offsettable_memref_p (dst))) + return false; + + /* Moving the first subword must not clobber a register + needed to move the second subword. */ + if (register_operand (dst, mode)) + { + rtx subreg = operand_subword (dst, first_subword, 0, mode); + if (reg_overlap_mentioned_p (subreg, src)) + return false; + } + + return true; +} + +/* Return true if it can be proven that [MEM1, MEM1 + SIZE] + and [MEM2, MEM2 + SIZE] do overlap and false + otherwise. */ + +bool +s390_overlap_p (rtx mem1, rtx mem2, HOST_WIDE_INT size) +{ + rtx addr1, addr2, addr_delta; + HOST_WIDE_INT delta; + + if (GET_CODE (mem1) != MEM || GET_CODE (mem2) != MEM) + return true; + + if (size == 0) + return false; + + addr1 = XEXP (mem1, 0); + addr2 = XEXP (mem2, 0); + + addr_delta = simplify_binary_operation (MINUS, Pmode, addr2, addr1); + + /* This overlapping check is used by peepholes merging memory block operations. + Overlapping operations would otherwise be recognized by the S/390 hardware + and would fall back to a slower implementation. Allowing overlapping + operations would lead to slow code but not to wrong code. Therefore we are + somewhat optimistic if we cannot prove that the memory blocks are + overlapping. + That's why we return false here although this may accept operations on + overlapping memory areas. */ + if (!addr_delta || GET_CODE (addr_delta) != CONST_INT) + return false; + + delta = INTVAL (addr_delta); + + if (delta == 0 + || (delta > 0 && delta < size) + || (delta < 0 && -delta < size)) + return true; + + return false; +} + +/* Check whether the address of memory reference MEM2 equals exactly + the address of memory reference MEM1 plus DELTA. Return true if + we can prove this to be the case, false otherwise. */ + +bool +s390_offset_p (rtx mem1, rtx mem2, rtx delta) +{ + rtx addr1, addr2, addr_delta; + + if (GET_CODE (mem1) != MEM || GET_CODE (mem2) != MEM) + return false; + + addr1 = XEXP (mem1, 0); + addr2 = XEXP (mem2, 0); + + addr_delta = simplify_binary_operation (MINUS, Pmode, addr2, addr1); + if (!addr_delta || !rtx_equal_p (addr_delta, delta)) + return false; + + return true; +} + +/* Expand logical operator CODE in mode MODE with operands OPERANDS. */ + +void +s390_expand_logical_operator (enum rtx_code code, enum machine_mode mode, + rtx *operands) +{ + enum machine_mode wmode = mode; + rtx dst = operands[0]; + rtx src1 = operands[1]; + rtx src2 = operands[2]; + rtx op, clob, tem; + + /* If we cannot handle the operation directly, use a temp register. */ + if (!s390_logical_operator_ok_p (operands)) + dst = gen_reg_rtx (mode); + + /* QImode and HImode patterns make sense only if we have a destination + in memory. Otherwise perform the operation in SImode. */ + if ((mode == QImode || mode == HImode) && GET_CODE (dst) != MEM) + wmode = SImode; + + /* Widen operands if required. */ + if (mode != wmode) + { + if (GET_CODE (dst) == SUBREG + && (tem = simplify_subreg (wmode, dst, mode, 0)) != 0) + dst = tem; + else if (REG_P (dst)) + dst = gen_rtx_SUBREG (wmode, dst, 0); + else + dst = gen_reg_rtx (wmode); + + if (GET_CODE (src1) == SUBREG + && (tem = simplify_subreg (wmode, src1, mode, 0)) != 0) + src1 = tem; + else if (GET_MODE (src1) != VOIDmode) + src1 = gen_rtx_SUBREG (wmode, force_reg (mode, src1), 0); + + if (GET_CODE (src2) == SUBREG + && (tem = simplify_subreg (wmode, src2, mode, 0)) != 0) + src2 = tem; + else if (GET_MODE (src2) != VOIDmode) + src2 = gen_rtx_SUBREG (wmode, force_reg (mode, src2), 0); + } + + /* Emit the instruction. */ + op = gen_rtx_SET (VOIDmode, dst, gen_rtx_fmt_ee (code, wmode, src1, src2)); + clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM)); + emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clob))); + + /* Fix up the destination if needed. */ + if (dst != operands[0]) + emit_move_insn (operands[0], gen_lowpart (mode, dst)); +} + +/* Check whether OPERANDS are OK for a logical operation (AND, IOR, XOR). */ + +bool +s390_logical_operator_ok_p (rtx *operands) +{ + /* If the destination operand is in memory, it needs to coincide + with one of the source operands. After reload, it has to be + the first source operand. */ + if (GET_CODE (operands[0]) == MEM) + return rtx_equal_p (operands[0], operands[1]) + || (!reload_completed && rtx_equal_p (operands[0], operands[2])); + + return true; +} + +/* Narrow logical operation CODE of memory operand MEMOP with immediate + operand IMMOP to switch from SS to SI type instructions. */ + +void +s390_narrow_logical_operator (enum rtx_code code, rtx *memop, rtx *immop) +{ + int def = code == AND ? -1 : 0; + HOST_WIDE_INT mask; + int part; + + gcc_assert (GET_CODE (*memop) == MEM); + gcc_assert (!MEM_VOLATILE_P (*memop)); + + mask = s390_extract_part (*immop, QImode, def); + part = s390_single_part (*immop, GET_MODE (*memop), QImode, def); + gcc_assert (part >= 0); + + *memop = adjust_address (*memop, QImode, part); + *immop = gen_int_mode (mask, QImode); +} + + +/* How to allocate a 'struct machine_function'. */ + +static struct machine_function * +s390_init_machine_status (void) +{ + return ggc_alloc_cleared_machine_function (); +} + +/* Change optimizations to be performed, depending on the + optimization level. */ + +static const struct default_options s390_option_optimization_table[] = + { + { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 }, + + /* ??? There are apparently still problems with -fcaller-saves. */ + { OPT_LEVELS_ALL, OPT_fcaller_saves, NULL, 0 }, + + /* Use MVCLE instructions to decrease code size if requested. */ + { OPT_LEVELS_SIZE, OPT_mmvcle, NULL, 1 }, + + { OPT_LEVELS_NONE, 0, NULL, 0 } + }; + +/* Implement TARGET_OPTION_INIT_STRUCT. */ + +static void +s390_option_init_struct (struct gcc_options *opts) +{ + /* By default, always emit DWARF-2 unwind info. This allows debugging + without maintaining a stack frame back-chain. */ + opts->x_flag_asynchronous_unwind_tables = 1; +} + +/* Return true if ARG is the name of a processor. Set *TYPE and *FLAGS + to the associated processor_type and processor_flags if so. */ + +static bool +s390_handle_arch_option (const char *arg, + enum processor_type *type, + int *flags) +{ + static struct pta + { + const char *const name; /* processor name or nickname. */ + const enum processor_type processor; + const int flags; /* From enum processor_flags. */ + } + const processor_alias_table[] = + { + {"g5", PROCESSOR_9672_G5, PF_IEEE_FLOAT}, + {"g6", PROCESSOR_9672_G6, PF_IEEE_FLOAT}, + {"z900", PROCESSOR_2064_Z900, PF_IEEE_FLOAT | PF_ZARCH}, + {"z990", PROCESSOR_2084_Z990, PF_IEEE_FLOAT | PF_ZARCH + | PF_LONG_DISPLACEMENT}, + {"z9-109", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH + | PF_LONG_DISPLACEMENT | PF_EXTIMM}, + {"z9-ec", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH + | PF_LONG_DISPLACEMENT | PF_EXTIMM | PF_DFP }, + {"z10", PROCESSOR_2097_Z10, PF_IEEE_FLOAT | PF_ZARCH + | PF_LONG_DISPLACEMENT | PF_EXTIMM | PF_DFP | PF_Z10}, + {"z196", PROCESSOR_2817_Z196, PF_IEEE_FLOAT | PF_ZARCH + | PF_LONG_DISPLACEMENT | PF_EXTIMM | PF_DFP | PF_Z10 | PF_Z196 }, + }; + size_t i; + + for (i = 0; i < ARRAY_SIZE (processor_alias_table); i++) + if (strcmp (arg, processor_alias_table[i].name) == 0) + { + *type = processor_alias_table[i].processor; + *flags = processor_alias_table[i].flags; + return true; + } + + *type = PROCESSOR_max; + *flags = 0; + return false; +} + +/* Implement TARGET_HANDLE_OPTION. */ + +static bool +s390_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED) +{ + switch (code) + { + case OPT_march_: + return s390_handle_arch_option (arg, &s390_arch, &s390_arch_flags); + + case OPT_mstack_guard_: + if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_guard) != 1) + return false; + if (exact_log2 (s390_stack_guard) == -1) + error ("stack guard value must be an exact power of 2"); + return true; + + case OPT_mstack_size_: + if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_size) != 1) + return false; + if (exact_log2 (s390_stack_size) == -1) + error ("stack size must be an exact power of 2"); + return true; + + case OPT_mtune_: + return s390_handle_arch_option (arg, &s390_tune, &s390_tune_flags); + + case OPT_mwarn_framesize_: + return sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_warn_framesize) == 1; + + default: + return true; + } +} + +static void +s390_option_override (void) +{ + /* Set up function hooks. */ + init_machine_status = s390_init_machine_status; + + /* Architecture mode defaults according to ABI. */ + if (!(target_flags_explicit & MASK_ZARCH)) + { + if (TARGET_64BIT) + target_flags |= MASK_ZARCH; + else + target_flags &= ~MASK_ZARCH; + } + + /* Determine processor architectural level. */ + if (!s390_arch_string) + { + s390_arch_string = TARGET_ZARCH? "z900" : "g5"; + s390_handle_arch_option (s390_arch_string, &s390_arch, &s390_arch_flags); + } + + /* This check is triggered when the user specified a wrong -march= + string and prevents subsequent error messages from being + issued. */ + if (s390_arch == PROCESSOR_max) + return; + + /* Determine processor to tune for. */ + if (s390_tune == PROCESSOR_max) + { + s390_tune = s390_arch; + s390_tune_flags = s390_arch_flags; + } + + /* Sanity checks. */ + if (TARGET_ZARCH && !TARGET_CPU_ZARCH) + error ("z/Architecture mode not supported on %s", s390_arch_string); + if (TARGET_64BIT && !TARGET_ZARCH) + error ("64-bit ABI not supported in ESA/390 mode"); + + if (TARGET_HARD_DFP && !TARGET_DFP) + { + if (target_flags_explicit & MASK_HARD_DFP) + { + if (!TARGET_CPU_DFP) + error ("hardware decimal floating point instructions" + " not available on %s", s390_arch_string); + if (!TARGET_ZARCH) + error ("hardware decimal floating point instructions" + " not available in ESA/390 mode"); + } + else + target_flags &= ~MASK_HARD_DFP; + } + + if ((target_flags_explicit & MASK_SOFT_FLOAT) && TARGET_SOFT_FLOAT) + { + if ((target_flags_explicit & MASK_HARD_DFP) && TARGET_HARD_DFP) + error ("-mhard-dfp can%'t be used in conjunction with -msoft-float"); + + target_flags &= ~MASK_HARD_DFP; + } + + /* Set processor cost function. */ + switch (s390_tune) + { + case PROCESSOR_2084_Z990: + s390_cost = &z990_cost; + break; + case PROCESSOR_2094_Z9_109: + s390_cost = &z9_109_cost; + break; + case PROCESSOR_2097_Z10: + s390_cost = &z10_cost; + case PROCESSOR_2817_Z196: + s390_cost = &z196_cost; + break; + default: + s390_cost = &z900_cost; + } + + if (TARGET_BACKCHAIN && TARGET_PACKED_STACK && TARGET_HARD_FLOAT) + error ("-mbackchain -mpacked-stack -mhard-float are not supported " + "in combination"); + + if (s390_stack_size) + { + if (s390_stack_guard >= s390_stack_size) + error ("stack size must be greater than the stack guard value"); + else if (s390_stack_size > 1 << 16) + error ("stack size must not be greater than 64k"); + } + else if (s390_stack_guard) + error ("-mstack-guard implies use of -mstack-size"); + +#ifdef TARGET_DEFAULT_LONG_DOUBLE_128 + if (!(target_flags_explicit & MASK_LONG_DOUBLE_128)) + target_flags |= MASK_LONG_DOUBLE_128; +#endif + + if (s390_tune == PROCESSOR_2097_Z10 + || s390_tune == PROCESSOR_2817_Z196) + { + maybe_set_param_value (PARAM_MAX_UNROLLED_INSNS, 100, + global_options.x_param_values, + global_options_set.x_param_values); + maybe_set_param_value (PARAM_MAX_UNROLL_TIMES, 32, + global_options.x_param_values, + global_options_set.x_param_values); + maybe_set_param_value (PARAM_MAX_COMPLETELY_PEELED_INSNS, 2000, + global_options.x_param_values, + global_options_set.x_param_values); + maybe_set_param_value (PARAM_MAX_COMPLETELY_PEEL_TIMES, 64, + global_options.x_param_values, + global_options_set.x_param_values); + } + + maybe_set_param_value (PARAM_MAX_PENDING_LIST_LENGTH, 256, + global_options.x_param_values, + global_options_set.x_param_values); + /* values for loop prefetching */ + maybe_set_param_value (PARAM_L1_CACHE_LINE_SIZE, 256, + global_options.x_param_values, + global_options_set.x_param_values); + maybe_set_param_value (PARAM_L1_CACHE_SIZE, 128, + global_options.x_param_values, + global_options_set.x_param_values); + /* s390 has more than 2 levels and the size is much larger. Since + we are always running virtualized assume that we only get a small + part of the caches above l1. */ + maybe_set_param_value (PARAM_L2_CACHE_SIZE, 1500, + global_options.x_param_values, + global_options_set.x_param_values); + maybe_set_param_value (PARAM_PREFETCH_MIN_INSN_TO_MEM_RATIO, 2, + global_options.x_param_values, + global_options_set.x_param_values); + maybe_set_param_value (PARAM_SIMULTANEOUS_PREFETCHES, 6, + global_options.x_param_values, + global_options_set.x_param_values); + + /* This cannot reside in s390_option_optimization_table since HAVE_prefetch + requires the arch flags to be evaluated already. Since prefetching + is beneficial on s390, we enable it if available. */ + if (flag_prefetch_loop_arrays < 0 && HAVE_prefetch && optimize >= 3) + flag_prefetch_loop_arrays = 1; +} + +/* Map for smallest class containing reg regno. */ + +const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] = +{ GENERAL_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, + ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, + ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, + ADDR_REGS, ADDR_REGS, ADDR_REGS, ADDR_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + FP_REGS, FP_REGS, FP_REGS, FP_REGS, + ADDR_REGS, CC_REGS, ADDR_REGS, ADDR_REGS, + ACCESS_REGS, ACCESS_REGS +}; + +/* Return attribute type of insn. */ + +static enum attr_type +s390_safe_attr_type (rtx insn) +{ + if (recog_memoized (insn) >= 0) + return get_attr_type (insn); + else + return TYPE_NONE; +} + +/* Return true if DISP is a valid short displacement. */ + +static bool +s390_short_displacement (rtx disp) +{ + /* No displacement is OK. */ + if (!disp) + return true; + + /* Without the long displacement facility we don't need to + distingiush between long and short displacement. */ + if (!TARGET_LONG_DISPLACEMENT) + return true; + + /* Integer displacement in range. */ + if (GET_CODE (disp) == CONST_INT) + return INTVAL (disp) >= 0 && INTVAL (disp) < 4096; + + /* GOT offset is not OK, the GOT can be large. */ + if (GET_CODE (disp) == CONST + && GET_CODE (XEXP (disp, 0)) == UNSPEC + && (XINT (XEXP (disp, 0), 1) == UNSPEC_GOT + || XINT (XEXP (disp, 0), 1) == UNSPEC_GOTNTPOFF)) + return false; + + /* All other symbolic constants are literal pool references, + which are OK as the literal pool must be small. */ + if (GET_CODE (disp) == CONST) + return true; + + return false; +} + +/* Decompose a RTL expression ADDR for a memory address into + its components, returned in OUT. + + Returns false if ADDR is not a valid memory address, true + otherwise. If OUT is NULL, don't return the components, + but check for validity only. + + Note: Only addresses in canonical form are recognized. + LEGITIMIZE_ADDRESS should convert non-canonical forms to the + canonical form so that they will be recognized. */ + +static int +s390_decompose_address (rtx addr, struct s390_address *out) +{ + HOST_WIDE_INT offset = 0; + rtx base = NULL_RTX; + rtx indx = NULL_RTX; + rtx disp = NULL_RTX; + rtx orig_disp; + bool pointer = false; + bool base_ptr = false; + bool indx_ptr = false; + bool literal_pool = false; + + /* We may need to substitute the literal pool base register into the address + below. However, at this point we do not know which register is going to + be used as base, so we substitute the arg pointer register. This is going + to be treated as holding a pointer below -- it shouldn't be used for any + other purpose. */ + rtx fake_pool_base = gen_rtx_REG (Pmode, ARG_POINTER_REGNUM); + + /* Decompose address into base + index + displacement. */ + + if (GET_CODE (addr) == REG || GET_CODE (addr) == UNSPEC) + base = addr; + + else if (GET_CODE (addr) == PLUS) + { + rtx op0 = XEXP (addr, 0); + rtx op1 = XEXP (addr, 1); + enum rtx_code code0 = GET_CODE (op0); + enum rtx_code code1 = GET_CODE (op1); + + if (code0 == REG || code0 == UNSPEC) + { + if (code1 == REG || code1 == UNSPEC) + { + indx = op0; /* index + base */ + base = op1; + } + + else + { + base = op0; /* base + displacement */ + disp = op1; + } + } + + else if (code0 == PLUS) + { + indx = XEXP (op0, 0); /* index + base + disp */ + base = XEXP (op0, 1); + disp = op1; + } + + else + { + return false; + } + } + + else + disp = addr; /* displacement */ + + /* Extract integer part of displacement. */ + orig_disp = disp; + if (disp) + { + if (GET_CODE (disp) == CONST_INT) + { + offset = INTVAL (disp); + disp = NULL_RTX; + } + else if (GET_CODE (disp) == CONST + && GET_CODE (XEXP (disp, 0)) == PLUS + && GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT) + { + offset = INTVAL (XEXP (XEXP (disp, 0), 1)); + disp = XEXP (XEXP (disp, 0), 0); + } + } + + /* Strip off CONST here to avoid special case tests later. */ + if (disp && GET_CODE (disp) == CONST) + disp = XEXP (disp, 0); + + /* We can convert literal pool addresses to + displacements by basing them off the base register. */ + if (disp && GET_CODE (disp) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (disp)) + { + /* Either base or index must be free to hold the base register. */ + if (!base) + base = fake_pool_base, literal_pool = true; + else if (!indx) + indx = fake_pool_base, literal_pool = true; + else + return false; + + /* Mark up the displacement. */ + disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp), + UNSPEC_LTREL_OFFSET); + } + + /* Validate base register. */ + if (base) + { + if (GET_CODE (base) == UNSPEC) + switch (XINT (base, 1)) + { + case UNSPEC_LTREF: + if (!disp) + disp = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, XVECEXP (base, 0, 0)), + UNSPEC_LTREL_OFFSET); + else + return false; + + base = XVECEXP (base, 0, 1); + break; + + case UNSPEC_LTREL_BASE: + if (XVECLEN (base, 0) == 1) + base = fake_pool_base, literal_pool = true; + else + base = XVECEXP (base, 0, 1); + break; + + default: + return false; + } + + if (!REG_P (base) + || (GET_MODE (base) != SImode + && GET_MODE (base) != Pmode)) + return false; + + if (REGNO (base) == STACK_POINTER_REGNUM + || REGNO (base) == FRAME_POINTER_REGNUM + || ((reload_completed || reload_in_progress) + && frame_pointer_needed + && REGNO (base) == HARD_FRAME_POINTER_REGNUM) + || REGNO (base) == ARG_POINTER_REGNUM + || (flag_pic + && REGNO (base) == PIC_OFFSET_TABLE_REGNUM)) + pointer = base_ptr = true; + + if ((reload_completed || reload_in_progress) + && base == cfun->machine->base_reg) + pointer = base_ptr = literal_pool = true; + } + + /* Validate index register. */ + if (indx) + { + if (GET_CODE (indx) == UNSPEC) + switch (XINT (indx, 1)) + { + case UNSPEC_LTREF: + if (!disp) + disp = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, XVECEXP (indx, 0, 0)), + UNSPEC_LTREL_OFFSET); + else + return false; + + indx = XVECEXP (indx, 0, 1); + break; + + case UNSPEC_LTREL_BASE: + if (XVECLEN (indx, 0) == 1) + indx = fake_pool_base, literal_pool = true; + else + indx = XVECEXP (indx, 0, 1); + break; + + default: + return false; + } + + if (!REG_P (indx) + || (GET_MODE (indx) != SImode + && GET_MODE (indx) != Pmode)) + return false; + + if (REGNO (indx) == STACK_POINTER_REGNUM + || REGNO (indx) == FRAME_POINTER_REGNUM + || ((reload_completed || reload_in_progress) + && frame_pointer_needed + && REGNO (indx) == HARD_FRAME_POINTER_REGNUM) + || REGNO (indx) == ARG_POINTER_REGNUM + || (flag_pic + && REGNO (indx) == PIC_OFFSET_TABLE_REGNUM)) + pointer = indx_ptr = true; + + if ((reload_completed || reload_in_progress) + && indx == cfun->machine->base_reg) + pointer = indx_ptr = literal_pool = true; + } + + /* Prefer to use pointer as base, not index. */ + if (base && indx && !base_ptr + && (indx_ptr || (!REG_POINTER (base) && REG_POINTER (indx)))) + { + rtx tmp = base; + base = indx; + indx = tmp; + } + + /* Validate displacement. */ + if (!disp) + { + /* If virtual registers are involved, the displacement will change later + anyway as the virtual registers get eliminated. This could make a + valid displacement invalid, but it is more likely to make an invalid + displacement valid, because we sometimes access the register save area + via negative offsets to one of those registers. + Thus we don't check the displacement for validity here. If after + elimination the displacement turns out to be invalid after all, + this is fixed up by reload in any case. */ + if (base != arg_pointer_rtx + && indx != arg_pointer_rtx + && base != return_address_pointer_rtx + && indx != return_address_pointer_rtx + && base != frame_pointer_rtx + && indx != frame_pointer_rtx + && base != virtual_stack_vars_rtx + && indx != virtual_stack_vars_rtx) + if (!DISP_IN_RANGE (offset)) + return false; + } + else + { + /* All the special cases are pointers. */ + pointer = true; + + /* In the small-PIC case, the linker converts @GOT + and @GOTNTPOFF offsets to possible displacements. */ + if (GET_CODE (disp) == UNSPEC + && (XINT (disp, 1) == UNSPEC_GOT + || XINT (disp, 1) == UNSPEC_GOTNTPOFF) + && flag_pic == 1) + { + ; + } + + /* Accept pool label offsets. */ + else if (GET_CODE (disp) == UNSPEC + && XINT (disp, 1) == UNSPEC_POOL_OFFSET) + ; + + /* Accept literal pool references. */ + else if (GET_CODE (disp) == UNSPEC + && XINT (disp, 1) == UNSPEC_LTREL_OFFSET) + { + /* In case CSE pulled a non literal pool reference out of + the pool we have to reject the address. This is + especially important when loading the GOT pointer on non + zarch CPUs. In this case the literal pool contains an lt + relative offset to the _GLOBAL_OFFSET_TABLE_ label which + will most likely exceed the displacement. */ + if (GET_CODE (XVECEXP (disp, 0, 0)) != SYMBOL_REF + || !CONSTANT_POOL_ADDRESS_P (XVECEXP (disp, 0, 0))) + return false; + + orig_disp = gen_rtx_CONST (Pmode, disp); + if (offset) + { + /* If we have an offset, make sure it does not + exceed the size of the constant pool entry. */ + rtx sym = XVECEXP (disp, 0, 0); + if (offset >= GET_MODE_SIZE (get_pool_mode (sym))) + return false; + + orig_disp = plus_constant (orig_disp, offset); + } + } + + else + return false; + } + + if (!base && !indx) + pointer = true; + + if (out) + { + out->base = base; + out->indx = indx; + out->disp = orig_disp; + out->pointer = pointer; + out->literal_pool = literal_pool; + } + + return true; +} + +/* Decompose a RTL expression OP for a shift count into its components, + and return the base register in BASE and the offset in OFFSET. + + Return true if OP is a valid shift count, false if not. */ + +bool +s390_decompose_shift_count (rtx op, rtx *base, HOST_WIDE_INT *offset) +{ + HOST_WIDE_INT off = 0; + + /* We can have an integer constant, an address register, + or a sum of the two. */ + if (GET_CODE (op) == CONST_INT) + { + off = INTVAL (op); + op = NULL_RTX; + } + if (op && GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT) + { + off = INTVAL (XEXP (op, 1)); + op = XEXP (op, 0); + } + while (op && GET_CODE (op) == SUBREG) + op = SUBREG_REG (op); + + if (op && GET_CODE (op) != REG) + return false; + + if (offset) + *offset = off; + if (base) + *base = op; + + return true; +} + + +/* Return true if CODE is a valid address without index. */ + +bool +s390_legitimate_address_without_index_p (rtx op) +{ + struct s390_address addr; + + if (!s390_decompose_address (XEXP (op, 0), &addr)) + return false; + if (addr.indx) + return false; + + return true; +} + + +/* Return true if ADDR is of kind symbol_ref or symbol_ref + const_int + and return these parts in SYMREF and ADDEND. You can pass NULL in + SYMREF and/or ADDEND if you are not interested in these values. + Literal pool references are *not* considered symbol references. */ + +static bool +s390_symref_operand_p (rtx addr, rtx *symref, HOST_WIDE_INT *addend) +{ + HOST_WIDE_INT tmpaddend = 0; + + if (GET_CODE (addr) == CONST) + addr = XEXP (addr, 0); + + if (GET_CODE (addr) == PLUS) + { + if (GET_CODE (XEXP (addr, 0)) == SYMBOL_REF + && !CONSTANT_POOL_ADDRESS_P (XEXP (addr, 0)) + && CONST_INT_P (XEXP (addr, 1))) + { + tmpaddend = INTVAL (XEXP (addr, 1)); + addr = XEXP (addr, 0); + } + else + return false; + } + else + if (GET_CODE (addr) != SYMBOL_REF || CONSTANT_POOL_ADDRESS_P (addr)) + return false; + + if (symref) + *symref = addr; + if (addend) + *addend = tmpaddend; + + return true; +} + + +/* Return true if the address in OP is valid for constraint letter C + if wrapped in a MEM rtx. Set LIT_POOL_OK to true if it literal + pool MEMs should be accepted. Only the Q, R, S, T constraint + letters are allowed for C. */ + +static int +s390_check_qrst_address (char c, rtx op, bool lit_pool_ok) +{ + struct s390_address addr; + bool decomposed = false; + + /* This check makes sure that no symbolic address (except literal + pool references) are accepted by the R or T constraints. */ + if (s390_symref_operand_p (op, NULL, NULL)) + return 0; + + /* Ensure literal pool references are only accepted if LIT_POOL_OK. */ + if (!lit_pool_ok) + { + if (!s390_decompose_address (op, &addr)) + return 0; + if (addr.literal_pool) + return 0; + decomposed = true; + } + + switch (c) + { + case 'Q': /* no index short displacement */ + if (!decomposed && !s390_decompose_address (op, &addr)) + return 0; + if (addr.indx) + return 0; + if (!s390_short_displacement (addr.disp)) + return 0; + break; + + case 'R': /* with index short displacement */ + if (TARGET_LONG_DISPLACEMENT) + { + if (!decomposed && !s390_decompose_address (op, &addr)) + return 0; + if (!s390_short_displacement (addr.disp)) + return 0; + } + /* Any invalid address here will be fixed up by reload, + so accept it for the most generic constraint. */ + break; + + case 'S': /* no index long displacement */ + if (!TARGET_LONG_DISPLACEMENT) + return 0; + if (!decomposed && !s390_decompose_address (op, &addr)) + return 0; + if (addr.indx) + return 0; + if (s390_short_displacement (addr.disp)) + return 0; + break; + + case 'T': /* with index long displacement */ + if (!TARGET_LONG_DISPLACEMENT) + return 0; + /* Any invalid address here will be fixed up by reload, + so accept it for the most generic constraint. */ + if ((decomposed || s390_decompose_address (op, &addr)) + && s390_short_displacement (addr.disp)) + return 0; + break; + default: + return 0; + } + return 1; +} + + +/* Evaluates constraint strings described by the regular expression + ([A|B|Z](Q|R|S|T))|U|W|Y and returns 1 if OP is a valid operand for + the constraint given in STR, or 0 else. */ + +int +s390_mem_constraint (const char *str, rtx op) +{ + char c = str[0]; + + switch (c) + { + case 'A': + /* Check for offsettable variants of memory constraints. */ + if (!MEM_P (op) || MEM_VOLATILE_P (op)) + return 0; + if ((reload_completed || reload_in_progress) + ? !offsettable_memref_p (op) : !offsettable_nonstrict_memref_p (op)) + return 0; + return s390_check_qrst_address (str[1], XEXP (op, 0), true); + case 'B': + /* Check for non-literal-pool variants of memory constraints. */ + if (!MEM_P (op)) + return 0; + return s390_check_qrst_address (str[1], XEXP (op, 0), false); + case 'Q': + case 'R': + case 'S': + case 'T': + if (GET_CODE (op) != MEM) + return 0; + return s390_check_qrst_address (c, XEXP (op, 0), true); + case 'U': + return (s390_check_qrst_address ('Q', op, true) + || s390_check_qrst_address ('R', op, true)); + case 'W': + return (s390_check_qrst_address ('S', op, true) + || s390_check_qrst_address ('T', op, true)); + case 'Y': + /* Simply check for the basic form of a shift count. Reload will + take care of making sure we have a proper base register. */ + if (!s390_decompose_shift_count (op, NULL, NULL)) + return 0; + break; + case 'Z': + return s390_check_qrst_address (str[1], op, true); + default: + return 0; + } + return 1; +} + + +/* Evaluates constraint strings starting with letter O. Input + parameter C is the second letter following the "O" in the constraint + string. Returns 1 if VALUE meets the respective constraint and 0 + otherwise. */ + +int +s390_O_constraint_str (const char c, HOST_WIDE_INT value) +{ + if (!TARGET_EXTIMM) + return 0; + + switch (c) + { + case 's': + return trunc_int_for_mode (value, SImode) == value; + + case 'p': + return value == 0 + || s390_single_part (GEN_INT (value), DImode, SImode, 0) == 1; + + case 'n': + return s390_single_part (GEN_INT (value - 1), DImode, SImode, -1) == 1; + + default: + gcc_unreachable (); + } +} + + +/* Evaluates constraint strings starting with letter N. Parameter STR + contains the letters following letter "N" in the constraint string. + Returns true if VALUE matches the constraint. */ + +int +s390_N_constraint_str (const char *str, HOST_WIDE_INT value) +{ + enum machine_mode mode, part_mode; + int def; + int part, part_goal; + + + if (str[0] == 'x') + part_goal = -1; + else + part_goal = str[0] - '0'; + + switch (str[1]) + { + case 'Q': + part_mode = QImode; + break; + case 'H': + part_mode = HImode; + break; + case 'S': + part_mode = SImode; + break; + default: + return 0; + } + + switch (str[2]) + { + case 'H': + mode = HImode; + break; + case 'S': + mode = SImode; + break; + case 'D': + mode = DImode; + break; + default: + return 0; + } + + switch (str[3]) + { + case '0': + def = 0; + break; + case 'F': + def = -1; + break; + default: + return 0; + } + + if (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (part_mode)) + return 0; + + part = s390_single_part (GEN_INT (value), mode, part_mode, def); + if (part < 0) + return 0; + if (part_goal != -1 && part_goal != part) + return 0; + + return 1; +} + + +/* Returns true if the input parameter VALUE is a float zero. */ + +int +s390_float_const_zero_p (rtx value) +{ + return (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT + && value == CONST0_RTX (GET_MODE (value))); +} + +/* Implement TARGET_REGISTER_MOVE_COST. */ + +static int +s390_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, + reg_class_t from, reg_class_t to) +{ +/* On s390, copy between fprs and gprs is expensive. */ + if ((reg_classes_intersect_p (from, GENERAL_REGS) + && reg_classes_intersect_p (to, FP_REGS)) + || (reg_classes_intersect_p (from, FP_REGS) + && reg_classes_intersect_p (to, GENERAL_REGS))) + return 10; + + return 1; +} + +/* Implement TARGET_MEMORY_MOVE_COST. */ + +static int +s390_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED, + reg_class_t rclass ATTRIBUTE_UNUSED, + bool in ATTRIBUTE_UNUSED) +{ + return 1; +} + +/* Compute a (partial) cost for rtx X. Return true if the complete + cost has been computed, and false if subexpressions should be + scanned. In either case, *TOTAL contains the cost result. + CODE contains GET_CODE (x), OUTER_CODE contains the code + of the superexpression of x. */ + +static bool +s390_rtx_costs (rtx x, int code, int outer_code, int *total, + bool speed ATTRIBUTE_UNUSED) +{ + switch (code) + { + case CONST: + case CONST_INT: + case LABEL_REF: + case SYMBOL_REF: + case CONST_DOUBLE: + case MEM: + *total = 0; + return true; + + case ASHIFT: + case ASHIFTRT: + case LSHIFTRT: + case ROTATE: + case ROTATERT: + case AND: + case IOR: + case XOR: + case NEG: + case NOT: + *total = COSTS_N_INSNS (1); + return false; + + case PLUS: + case MINUS: + *total = COSTS_N_INSNS (1); + return false; + + case MULT: + switch (GET_MODE (x)) + { + case SImode: + { + rtx left = XEXP (x, 0); + rtx right = XEXP (x, 1); + if (GET_CODE (right) == CONST_INT + && CONST_OK_FOR_K (INTVAL (right))) + *total = s390_cost->mhi; + else if (GET_CODE (left) == SIGN_EXTEND) + *total = s390_cost->mh; + else + *total = s390_cost->ms; /* msr, ms, msy */ + break; + } + case DImode: + { + rtx left = XEXP (x, 0); + rtx right = XEXP (x, 1); + if (TARGET_ZARCH) + { + if (GET_CODE (right) == CONST_INT + && CONST_OK_FOR_K (INTVAL (right))) + *total = s390_cost->mghi; + else if (GET_CODE (left) == SIGN_EXTEND) + *total = s390_cost->msgf; + else + *total = s390_cost->msg; /* msgr, msg */ + } + else /* TARGET_31BIT */ + { + if (GET_CODE (left) == SIGN_EXTEND + && GET_CODE (right) == SIGN_EXTEND) + /* mulsidi case: mr, m */ + *total = s390_cost->m; + else if (GET_CODE (left) == ZERO_EXTEND + && GET_CODE (right) == ZERO_EXTEND + && TARGET_CPU_ZARCH) + /* umulsidi case: ml, mlr */ + *total = s390_cost->ml; + else + /* Complex calculation is required. */ + *total = COSTS_N_INSNS (40); + } + break; + } + case SFmode: + case DFmode: + *total = s390_cost->mult_df; + break; + case TFmode: + *total = s390_cost->mxbr; + break; + default: + return false; + } + return false; + + case FMA: + switch (GET_MODE (x)) + { + case DFmode: + *total = s390_cost->madbr; + break; + case SFmode: + *total = s390_cost->maebr; + break; + default: + return false; + } + /* Negate in the third argument is free: FMSUB. */ + if (GET_CODE (XEXP (x, 2)) == NEG) + { + *total += (rtx_cost (XEXP (x, 0), FMA, speed) + + rtx_cost (XEXP (x, 1), FMA, speed) + + rtx_cost (XEXP (XEXP (x, 2), 0), FMA, speed)); + return true; + } + return false; + + case UDIV: + case UMOD: + if (GET_MODE (x) == TImode) /* 128 bit division */ + *total = s390_cost->dlgr; + else if (GET_MODE (x) == DImode) + { + rtx right = XEXP (x, 1); + if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */ + *total = s390_cost->dlr; + else /* 64 by 64 bit division */ + *total = s390_cost->dlgr; + } + else if (GET_MODE (x) == SImode) /* 32 bit division */ + *total = s390_cost->dlr; + return false; + + case DIV: + case MOD: + if (GET_MODE (x) == DImode) + { + rtx right = XEXP (x, 1); + if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */ + if (TARGET_ZARCH) + *total = s390_cost->dsgfr; + else + *total = s390_cost->dr; + else /* 64 by 64 bit division */ + *total = s390_cost->dsgr; + } + else if (GET_MODE (x) == SImode) /* 32 bit division */ + *total = s390_cost->dlr; + else if (GET_MODE (x) == SFmode) + { + *total = s390_cost->debr; + } + else if (GET_MODE (x) == DFmode) + { + *total = s390_cost->ddbr; + } + else if (GET_MODE (x) == TFmode) + { + *total = s390_cost->dxbr; + } + return false; + + case SQRT: + if (GET_MODE (x) == SFmode) + *total = s390_cost->sqebr; + else if (GET_MODE (x) == DFmode) + *total = s390_cost->sqdbr; + else /* TFmode */ + *total = s390_cost->sqxbr; + return false; + + case SIGN_EXTEND: + case ZERO_EXTEND: + if (outer_code == MULT || outer_code == DIV || outer_code == MOD + || outer_code == PLUS || outer_code == MINUS + || outer_code == COMPARE) + *total = 0; + return false; + + case COMPARE: + *total = COSTS_N_INSNS (1); + if (GET_CODE (XEXP (x, 0)) == AND + && GET_CODE (XEXP (x, 1)) == CONST_INT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT) + { + rtx op0 = XEXP (XEXP (x, 0), 0); + rtx op1 = XEXP (XEXP (x, 0), 1); + rtx op2 = XEXP (x, 1); + + if (memory_operand (op0, GET_MODE (op0)) + && s390_tm_ccmode (op1, op2, 0) != VOIDmode) + return true; + if (register_operand (op0, GET_MODE (op0)) + && s390_tm_ccmode (op1, op2, 1) != VOIDmode) + return true; + } + return false; + + default: + return false; + } +} + +/* Return the cost of an address rtx ADDR. */ + +static int +s390_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED) +{ + struct s390_address ad; + if (!s390_decompose_address (addr, &ad)) + return 1000; + + return ad.indx? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (1); +} + +/* If OP is a SYMBOL_REF of a thread-local symbol, return its TLS mode, + otherwise return 0. */ + +int +tls_symbolic_operand (rtx op) +{ + if (GET_CODE (op) != SYMBOL_REF) + return 0; + return SYMBOL_REF_TLS_MODEL (op); +} + +/* Split DImode access register reference REG (on 64-bit) into its constituent + low and high parts, and store them into LO and HI. Note that gen_lowpart/ + gen_highpart cannot be used as they assume all registers are word-sized, + while our access registers have only half that size. */ + +void +s390_split_access_reg (rtx reg, rtx *lo, rtx *hi) +{ + gcc_assert (TARGET_64BIT); + gcc_assert (ACCESS_REG_P (reg)); + gcc_assert (GET_MODE (reg) == DImode); + gcc_assert (!(REGNO (reg) & 1)); + + *lo = gen_rtx_REG (SImode, REGNO (reg) + 1); + *hi = gen_rtx_REG (SImode, REGNO (reg)); +} + +/* Return true if OP contains a symbol reference */ + +bool +symbolic_reference_mentioned_p (rtx op) +{ + const char *fmt; + int i; + + if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) + return 1; + + fmt = GET_RTX_FORMAT (GET_CODE (op)); + for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + int j; + + for (j = XVECLEN (op, i) - 1; j >= 0; j--) + if (symbolic_reference_mentioned_p (XVECEXP (op, i, j))) + return 1; + } + + else if (fmt[i] == 'e' && symbolic_reference_mentioned_p (XEXP (op, i))) + return 1; + } + + return 0; +} + +/* Return true if OP contains a reference to a thread-local symbol. */ + +bool +tls_symbolic_reference_mentioned_p (rtx op) +{ + const char *fmt; + int i; + + if (GET_CODE (op) == SYMBOL_REF) + return tls_symbolic_operand (op); + + fmt = GET_RTX_FORMAT (GET_CODE (op)); + for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + int j; + + for (j = XVECLEN (op, i) - 1; j >= 0; j--) + if (tls_symbolic_reference_mentioned_p (XVECEXP (op, i, j))) + return true; + } + + else if (fmt[i] == 'e' && tls_symbolic_reference_mentioned_p (XEXP (op, i))) + return true; + } + + return false; +} + + +/* Return true if OP is a legitimate general operand when + generating PIC code. It is given that flag_pic is on + and that OP satisfies CONSTANT_P or is a CONST_DOUBLE. */ + +int +legitimate_pic_operand_p (rtx op) +{ + /* Accept all non-symbolic constants. */ + if (!SYMBOLIC_CONST (op)) + return 1; + + /* Reject everything else; must be handled + via emit_symbolic_move. */ + return 0; +} + +/* Returns true if the constant value OP is a legitimate general operand. + It is given that OP satisfies CONSTANT_P or is a CONST_DOUBLE. */ + +int +legitimate_constant_p (rtx op) +{ + /* Accept all non-symbolic constants. */ + if (!SYMBOLIC_CONST (op)) + return 1; + + /* Accept immediate LARL operands. */ + if (TARGET_CPU_ZARCH && larl_operand (op, VOIDmode)) + return 1; + + /* Thread-local symbols are never legal constants. This is + so that emit_call knows that computing such addresses + might require a function call. */ + if (TLS_SYMBOLIC_CONST (op)) + return 0; + + /* In the PIC case, symbolic constants must *not* be + forced into the literal pool. We accept them here, + so that they will be handled by emit_symbolic_move. */ + if (flag_pic) + return 1; + + /* All remaining non-PIC symbolic constants are + forced into the literal pool. */ + return 0; +} + +/* Determine if it's legal to put X into the constant pool. This + is not possible if X contains the address of a symbol that is + not constant (TLS) or not known at final link time (PIC). */ + +static bool +s390_cannot_force_const_mem (rtx x) +{ + switch (GET_CODE (x)) + { + case CONST_INT: + case CONST_DOUBLE: + /* Accept all non-symbolic constants. */ + return false; + + case LABEL_REF: + /* Labels are OK iff we are non-PIC. */ + return flag_pic != 0; + + case SYMBOL_REF: + /* 'Naked' TLS symbol references are never OK, + non-TLS symbols are OK iff we are non-PIC. */ + if (tls_symbolic_operand (x)) + return true; + else + return flag_pic != 0; + + case CONST: + return s390_cannot_force_const_mem (XEXP (x, 0)); + case PLUS: + case MINUS: + return s390_cannot_force_const_mem (XEXP (x, 0)) + || s390_cannot_force_const_mem (XEXP (x, 1)); + + case UNSPEC: + switch (XINT (x, 1)) + { + /* Only lt-relative or GOT-relative UNSPECs are OK. */ + case UNSPEC_LTREL_OFFSET: + case UNSPEC_GOT: + case UNSPEC_GOTOFF: + case UNSPEC_PLTOFF: + case UNSPEC_TLSGD: + case UNSPEC_TLSLDM: + case UNSPEC_NTPOFF: + case UNSPEC_DTPOFF: + case UNSPEC_GOTNTPOFF: + case UNSPEC_INDNTPOFF: + return false; + + /* If the literal pool shares the code section, be put + execute template placeholders into the pool as well. */ + case UNSPEC_INSN: + return TARGET_CPU_ZARCH; + + default: + return true; + } + break; + + default: + gcc_unreachable (); + } +} + +/* Returns true if the constant value OP is a legitimate general + operand during and after reload. The difference to + legitimate_constant_p is that this function will not accept + a constant that would need to be forced to the literal pool + before it can be used as operand. + This function accepts all constants which can be loaded directly + into a GPR. */ + +bool +legitimate_reload_constant_p (rtx op) +{ + /* Accept la(y) operands. */ + if (GET_CODE (op) == CONST_INT + && DISP_IN_RANGE (INTVAL (op))) + return true; + + /* Accept l(g)hi/l(g)fi operands. */ + if (GET_CODE (op) == CONST_INT + && (CONST_OK_FOR_K (INTVAL (op)) || CONST_OK_FOR_Os (INTVAL (op)))) + return true; + + /* Accept lliXX operands. */ + if (TARGET_ZARCH + && GET_CODE (op) == CONST_INT + && trunc_int_for_mode (INTVAL (op), word_mode) == INTVAL (op) + && s390_single_part (op, word_mode, HImode, 0) >= 0) + return true; + + if (TARGET_EXTIMM + && GET_CODE (op) == CONST_INT + && trunc_int_for_mode (INTVAL (op), word_mode) == INTVAL (op) + && s390_single_part (op, word_mode, SImode, 0) >= 0) + return true; + + /* Accept larl operands. */ + if (TARGET_CPU_ZARCH + && larl_operand (op, VOIDmode)) + return true; + + /* Accept floating-point zero operands that fit into a single GPR. */ + if (GET_CODE (op) == CONST_DOUBLE + && s390_float_const_zero_p (op) + && GET_MODE_SIZE (GET_MODE (op)) <= UNITS_PER_WORD) + return true; + + /* Accept double-word operands that can be split. */ + if (GET_CODE (op) == CONST_INT + && trunc_int_for_mode (INTVAL (op), word_mode) != INTVAL (op)) + { + enum machine_mode dword_mode = word_mode == SImode ? DImode : TImode; + rtx hi = operand_subword (op, 0, 0, dword_mode); + rtx lo = operand_subword (op, 1, 0, dword_mode); + return legitimate_reload_constant_p (hi) + && legitimate_reload_constant_p (lo); + } + + /* Everything else cannot be handled without reload. */ + return false; +} + +/* Returns true if the constant value OP is a legitimate fp operand + during and after reload. + This function accepts all constants which can be loaded directly + into an FPR. */ + +static bool +legitimate_reload_fp_constant_p (rtx op) +{ + /* Accept floating-point zero operands if the load zero instruction + can be used. */ + if (TARGET_Z196 + && GET_CODE (op) == CONST_DOUBLE + && s390_float_const_zero_p (op)) + return true; + + return false; +} + +/* Given an rtx OP being reloaded into a reg required to be in class RCLASS, + return the class of reg to actually use. */ + +static reg_class_t +s390_preferred_reload_class (rtx op, reg_class_t rclass) +{ + switch (GET_CODE (op)) + { + /* Constants we cannot reload into general registers + must be forced into the literal pool. */ + case CONST_DOUBLE: + case CONST_INT: + if (reg_class_subset_p (GENERAL_REGS, rclass) + && legitimate_reload_constant_p (op)) + return GENERAL_REGS; + else if (reg_class_subset_p (ADDR_REGS, rclass) + && legitimate_reload_constant_p (op)) + return ADDR_REGS; + else if (reg_class_subset_p (FP_REGS, rclass) + && legitimate_reload_fp_constant_p (op)) + return FP_REGS; + return NO_REGS; + + /* If a symbolic constant or a PLUS is reloaded, + it is most likely being used as an address, so + prefer ADDR_REGS. If 'class' is not a superset + of ADDR_REGS, e.g. FP_REGS, reject this reload. */ + case LABEL_REF: + case SYMBOL_REF: + case CONST: + if (reg_class_subset_p (ADDR_REGS, rclass) + && legitimate_reload_constant_p (op)) + return ADDR_REGS; + else + return NO_REGS; + case PLUS: + /* load address will be used for this reload. */ + if (reg_class_subset_p (ADDR_REGS, rclass)) + return ADDR_REGS; + else + return NO_REGS; + + default: + break; + } + + return rclass; +} + +/* Return true if ADDR is SYMBOL_REF + addend with addend being a + multiple of ALIGNMENT and the SYMBOL_REF being naturally + aligned. */ + +bool +s390_check_symref_alignment (rtx addr, HOST_WIDE_INT alignment) +{ + HOST_WIDE_INT addend; + rtx symref; + + if (!s390_symref_operand_p (addr, &symref, &addend)) + return false; + + return (!SYMBOL_REF_NOT_NATURALLY_ALIGNED_P (symref) + && !(addend & (alignment - 1))); +} + +/* ADDR is moved into REG using larl. If ADDR isn't a valid larl + operand SCRATCH is used to reload the even part of the address and + adding one. */ + +void +s390_reload_larl_operand (rtx reg, rtx addr, rtx scratch) +{ + HOST_WIDE_INT addend; + rtx symref; + + if (!s390_symref_operand_p (addr, &symref, &addend)) + gcc_unreachable (); + + if (!(addend & 1)) + /* Easy case. The addend is even so larl will do fine. */ + emit_move_insn (reg, addr); + else + { + /* We can leave the scratch register untouched if the target + register is a valid base register. */ + if (REGNO (reg) < FIRST_PSEUDO_REGISTER + && REGNO_REG_CLASS (REGNO (reg)) == ADDR_REGS) + scratch = reg; + + gcc_assert (REGNO (scratch) < FIRST_PSEUDO_REGISTER); + gcc_assert (REGNO_REG_CLASS (REGNO (scratch)) == ADDR_REGS); + + if (addend != 1) + emit_move_insn (scratch, + gen_rtx_CONST (Pmode, + gen_rtx_PLUS (Pmode, symref, + GEN_INT (addend - 1)))); + else + emit_move_insn (scratch, symref); + + /* Increment the address using la in order to avoid clobbering cc. */ + emit_move_insn (reg, gen_rtx_PLUS (Pmode, scratch, const1_rtx)); + } +} + +/* Generate what is necessary to move between REG and MEM using + SCRATCH. The direction is given by TOMEM. */ + +void +s390_reload_symref_address (rtx reg, rtx mem, rtx scratch, bool tomem) +{ + /* Reload might have pulled a constant out of the literal pool. + Force it back in. */ + if (CONST_INT_P (mem) || GET_CODE (mem) == CONST_DOUBLE + || GET_CODE (mem) == CONST) + mem = force_const_mem (GET_MODE (reg), mem); + + gcc_assert (MEM_P (mem)); + + /* For a load from memory we can leave the scratch register + untouched if the target register is a valid base register. */ + if (!tomem + && REGNO (reg) < FIRST_PSEUDO_REGISTER + && REGNO_REG_CLASS (REGNO (reg)) == ADDR_REGS + && GET_MODE (reg) == GET_MODE (scratch)) + scratch = reg; + + /* Load address into scratch register. Since we can't have a + secondary reload for a secondary reload we have to cover the case + where larl would need a secondary reload here as well. */ + s390_reload_larl_operand (scratch, XEXP (mem, 0), scratch); + + /* Now we can use a standard load/store to do the move. */ + if (tomem) + emit_move_insn (replace_equiv_address (mem, scratch), reg); + else + emit_move_insn (reg, replace_equiv_address (mem, scratch)); +} + +/* Inform reload about cases where moving X with a mode MODE to a register in + RCLASS requires an extra scratch or immediate register. Return the class + needed for the immediate register. */ + +static reg_class_t +s390_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i, + enum machine_mode mode, secondary_reload_info *sri) +{ + enum reg_class rclass = (enum reg_class) rclass_i; + + /* Intermediate register needed. */ + if (reg_classes_intersect_p (CC_REGS, rclass)) + return GENERAL_REGS; + + if (TARGET_Z10) + { + HOST_WIDE_INT offset; + rtx symref; + + /* On z10 several optimizer steps may generate larl operands with + an odd addend. */ + if (in_p + && s390_symref_operand_p (x, &symref, &offset) + && mode == Pmode + && !SYMBOL_REF_ALIGN1_P (symref) + && (offset & 1) == 1) + sri->icode = ((mode == DImode) ? CODE_FOR_reloaddi_larl_odd_addend_z10 + : CODE_FOR_reloadsi_larl_odd_addend_z10); + + /* On z10 we need a scratch register when moving QI, TI or floating + point mode values from or to a memory location with a SYMBOL_REF + or if the symref addend of a SI or DI move is not aligned to the + width of the access. */ + if (MEM_P (x) + && s390_symref_operand_p (XEXP (x, 0), NULL, NULL) + && (mode == QImode || mode == TImode || FLOAT_MODE_P (mode) + || (!TARGET_ZARCH && mode == DImode) + || ((mode == HImode || mode == SImode || mode == DImode) + && (!s390_check_symref_alignment (XEXP (x, 0), + GET_MODE_SIZE (mode)))))) + { +#define __SECONDARY_RELOAD_CASE(M,m) \ + case M##mode: \ + if (TARGET_64BIT) \ + sri->icode = in_p ? CODE_FOR_reload##m##di_toreg_z10 : \ + CODE_FOR_reload##m##di_tomem_z10; \ + else \ + sri->icode = in_p ? CODE_FOR_reload##m##si_toreg_z10 : \ + CODE_FOR_reload##m##si_tomem_z10; \ + break; + + switch (GET_MODE (x)) + { + __SECONDARY_RELOAD_CASE (QI, qi); + __SECONDARY_RELOAD_CASE (HI, hi); + __SECONDARY_RELOAD_CASE (SI, si); + __SECONDARY_RELOAD_CASE (DI, di); + __SECONDARY_RELOAD_CASE (TI, ti); + __SECONDARY_RELOAD_CASE (SF, sf); + __SECONDARY_RELOAD_CASE (DF, df); + __SECONDARY_RELOAD_CASE (TF, tf); + __SECONDARY_RELOAD_CASE (SD, sd); + __SECONDARY_RELOAD_CASE (DD, dd); + __SECONDARY_RELOAD_CASE (TD, td); + + default: + gcc_unreachable (); + } +#undef __SECONDARY_RELOAD_CASE + } + } + + /* We need a scratch register when loading a PLUS expression which + is not a legitimate operand of the LOAD ADDRESS instruction. */ + if (in_p && s390_plus_operand (x, mode)) + sri->icode = (TARGET_64BIT ? + CODE_FOR_reloaddi_plus : CODE_FOR_reloadsi_plus); + + /* Performing a multiword move from or to memory we have to make sure the + second chunk in memory is addressable without causing a displacement + overflow. If that would be the case we calculate the address in + a scratch register. */ + if (MEM_P (x) + && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && !DISP_IN_RANGE (INTVAL (XEXP (XEXP (x, 0), 1)) + + GET_MODE_SIZE (mode) - 1)) + { + /* For GENERAL_REGS a displacement overflow is no problem if occurring + in a s_operand address since we may fallback to lm/stm. So we only + have to care about overflows in the b+i+d case. */ + if ((reg_classes_intersect_p (GENERAL_REGS, rclass) + && s390_class_max_nregs (GENERAL_REGS, mode) > 1 + && GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS) + /* For FP_REGS no lm/stm is available so this check is triggered + for displacement overflows in b+i+d and b+d like addresses. */ + || (reg_classes_intersect_p (FP_REGS, rclass) + && s390_class_max_nregs (FP_REGS, mode) > 1)) + { + if (in_p) + sri->icode = (TARGET_64BIT ? + CODE_FOR_reloaddi_nonoffmem_in : + CODE_FOR_reloadsi_nonoffmem_in); + else + sri->icode = (TARGET_64BIT ? + CODE_FOR_reloaddi_nonoffmem_out : + CODE_FOR_reloadsi_nonoffmem_out); + } + } + + /* A scratch address register is needed when a symbolic constant is + copied to r0 compiling with -fPIC. In other cases the target + register might be used as temporary (see legitimize_pic_address). */ + if (in_p && SYMBOLIC_CONST (x) && flag_pic == 2 && rclass != ADDR_REGS) + sri->icode = (TARGET_64BIT ? + CODE_FOR_reloaddi_PIC_addr : + CODE_FOR_reloadsi_PIC_addr); + + /* Either scratch or no register needed. */ + return NO_REGS; +} + +/* Generate code to load SRC, which is PLUS that is not a + legitimate operand for the LA instruction, into TARGET. + SCRATCH may be used as scratch register. */ + +void +s390_expand_plus_operand (rtx target, rtx src, + rtx scratch) +{ + rtx sum1, sum2; + struct s390_address ad; + + /* src must be a PLUS; get its two operands. */ + gcc_assert (GET_CODE (src) == PLUS); + gcc_assert (GET_MODE (src) == Pmode); + + /* Check if any of the two operands is already scheduled + for replacement by reload. This can happen e.g. when + float registers occur in an address. */ + sum1 = find_replacement (&XEXP (src, 0)); + sum2 = find_replacement (&XEXP (src, 1)); + src = gen_rtx_PLUS (Pmode, sum1, sum2); + + /* If the address is already strictly valid, there's nothing to do. */ + if (!s390_decompose_address (src, &ad) + || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base))) + || (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx)))) + { + /* Otherwise, one of the operands cannot be an address register; + we reload its value into the scratch register. */ + if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15) + { + emit_move_insn (scratch, sum1); + sum1 = scratch; + } + if (true_regnum (sum2) < 1 || true_regnum (sum2) > 15) + { + emit_move_insn (scratch, sum2); + sum2 = scratch; + } + + /* According to the way these invalid addresses are generated + in reload.c, it should never happen (at least on s390) that + *neither* of the PLUS components, after find_replacements + was applied, is an address register. */ + if (sum1 == scratch && sum2 == scratch) + { + debug_rtx (src); + gcc_unreachable (); + } + + src = gen_rtx_PLUS (Pmode, sum1, sum2); + } + + /* Emit the LOAD ADDRESS pattern. Note that reload of PLUS + is only ever performed on addresses, so we can mark the + sum as legitimate for LA in any case. */ + s390_load_address (target, src); +} + + +/* Return true if ADDR is a valid memory address. + STRICT specifies whether strict register checking applies. */ + +static bool +s390_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict) +{ + struct s390_address ad; + + if (TARGET_Z10 + && larl_operand (addr, VOIDmode) + && (mode == VOIDmode + || s390_check_symref_alignment (addr, GET_MODE_SIZE (mode)))) + return true; + + if (!s390_decompose_address (addr, &ad)) + return false; + + if (strict) + { + if (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base))) + return false; + + if (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx))) + return false; + } + else + { + if (ad.base + && !(REGNO (ad.base) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (ad.base)) == ADDR_REGS)) + return false; + + if (ad.indx + && !(REGNO (ad.indx) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (ad.indx)) == ADDR_REGS)) + return false; + } + return true; +} + +/* Return true if OP is a valid operand for the LA instruction. + In 31-bit, we need to prove that the result is used as an + address, as LA performs only a 31-bit addition. */ + +bool +legitimate_la_operand_p (rtx op) +{ + struct s390_address addr; + if (!s390_decompose_address (op, &addr)) + return false; + + return (TARGET_64BIT || addr.pointer); +} + +/* Return true if it is valid *and* preferable to use LA to + compute the sum of OP1 and OP2. */ + +bool +preferred_la_operand_p (rtx op1, rtx op2) +{ + struct s390_address addr; + + if (op2 != const0_rtx) + op1 = gen_rtx_PLUS (Pmode, op1, op2); + + if (!s390_decompose_address (op1, &addr)) + return false; + if (addr.base && !REGNO_OK_FOR_BASE_P (REGNO (addr.base))) + return false; + if (addr.indx && !REGNO_OK_FOR_INDEX_P (REGNO (addr.indx))) + return false; + + /* Avoid LA instructions with index register on z196; it is + preferable to use regular add instructions when possible. */ + if (addr.indx && s390_tune == PROCESSOR_2817_Z196) + return false; + + if (!TARGET_64BIT && !addr.pointer) + return false; + + if (addr.pointer) + return true; + + if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base)) + || (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx))) + return true; + + return false; +} + +/* Emit a forced load-address operation to load SRC into DST. + This will use the LOAD ADDRESS instruction even in situations + where legitimate_la_operand_p (SRC) returns false. */ + +void +s390_load_address (rtx dst, rtx src) +{ + if (TARGET_64BIT) + emit_move_insn (dst, src); + else + emit_insn (gen_force_la_31 (dst, src)); +} + +/* Return a legitimate reference for ORIG (an address) using the + register REG. If REG is 0, a new pseudo is generated. + + There are two types of references that must be handled: + + 1. Global data references must load the address from the GOT, via + the PIC reg. An insn is emitted to do this load, and the reg is + returned. + + 2. Static data references, constant pool addresses, and code labels + compute the address as an offset from the GOT, whose base is in + the PIC reg. Static data objects have SYMBOL_FLAG_LOCAL set to + differentiate them from global data objects. The returned + address is the PIC reg + an unspec constant. + + TARGET_LEGITIMIZE_ADDRESS_P rejects symbolic references unless the PIC + reg also appears in the address. */ + +rtx +legitimize_pic_address (rtx orig, rtx reg) +{ + rtx addr = orig; + rtx new_rtx = orig; + rtx base; + + gcc_assert (!TLS_SYMBOLIC_CONST (addr)); + + if (GET_CODE (addr) == LABEL_REF + || (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (addr))) + { + /* This is a local symbol. */ + if (TARGET_CPU_ZARCH && larl_operand (addr, VOIDmode)) + { + /* Access local symbols PC-relative via LARL. + This is the same as in the non-PIC case, so it is + handled automatically ... */ + } + else + { + /* Access local symbols relative to the GOT. */ + + rtx temp = reg? reg : gen_reg_rtx (Pmode); + + if (reload_in_progress || reload_completed) + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); + + addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTOFF); + addr = gen_rtx_CONST (Pmode, addr); + addr = force_const_mem (Pmode, addr); + emit_move_insn (temp, addr); + + new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp); + if (reg != 0) + { + s390_load_address (reg, new_rtx); + new_rtx = reg; + } + } + } + else if (GET_CODE (addr) == SYMBOL_REF) + { + if (reg == 0) + reg = gen_reg_rtx (Pmode); + + if (flag_pic == 1) + { + /* Assume GOT offset < 4k. This is handled the same way + in both 31- and 64-bit code (@GOT). */ + + if (reload_in_progress || reload_completed) + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); + + new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT); + new_rtx = gen_rtx_CONST (Pmode, new_rtx); + new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new_rtx); + new_rtx = gen_const_mem (Pmode, new_rtx); + emit_move_insn (reg, new_rtx); + new_rtx = reg; + } + else if (TARGET_CPU_ZARCH) + { + /* If the GOT offset might be >= 4k, we determine the position + of the GOT entry via a PC-relative LARL (@GOTENT). */ + + rtx temp = reg ? reg : gen_reg_rtx (Pmode); + + gcc_assert (REGNO (temp) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (temp)) == ADDR_REGS); + + new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTENT); + new_rtx = gen_rtx_CONST (Pmode, new_rtx); + emit_move_insn (temp, new_rtx); + + new_rtx = gen_const_mem (Pmode, temp); + emit_move_insn (reg, new_rtx); + new_rtx = reg; + } + else + { + /* If the GOT offset might be >= 4k, we have to load it + from the literal pool (@GOT). */ + + rtx temp = reg ? reg : gen_reg_rtx (Pmode); + + gcc_assert (REGNO (temp) >= FIRST_PSEUDO_REGISTER + || REGNO_REG_CLASS (REGNO (temp)) == ADDR_REGS); + + if (reload_in_progress || reload_completed) + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); + + addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT); + addr = gen_rtx_CONST (Pmode, addr); + addr = force_const_mem (Pmode, addr); + emit_move_insn (temp, addr); + + new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp); + new_rtx = gen_const_mem (Pmode, new_rtx); + emit_move_insn (reg, new_rtx); + new_rtx = reg; + } + } + else + { + if (GET_CODE (addr) == CONST) + { + addr = XEXP (addr, 0); + if (GET_CODE (addr) == UNSPEC) + { + gcc_assert (XVECLEN (addr, 0) == 1); + switch (XINT (addr, 1)) + { + /* If someone moved a GOT-relative UNSPEC + out of the literal pool, force them back in. */ + case UNSPEC_GOTOFF: + case UNSPEC_PLTOFF: + new_rtx = force_const_mem (Pmode, orig); + break; + + /* @GOT is OK as is if small. */ + case UNSPEC_GOT: + if (flag_pic == 2) + new_rtx = force_const_mem (Pmode, orig); + break; + + /* @GOTENT is OK as is. */ + case UNSPEC_GOTENT: + break; + + /* @PLT is OK as is on 64-bit, must be converted to + GOT-relative @PLTOFF on 31-bit. */ + case UNSPEC_PLT: + if (!TARGET_CPU_ZARCH) + { + rtx temp = reg? reg : gen_reg_rtx (Pmode); + + if (reload_in_progress || reload_completed) + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); + + addr = XVECEXP (addr, 0, 0); + addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), + UNSPEC_PLTOFF); + addr = gen_rtx_CONST (Pmode, addr); + addr = force_const_mem (Pmode, addr); + emit_move_insn (temp, addr); + + new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp); + if (reg != 0) + { + s390_load_address (reg, new_rtx); + new_rtx = reg; + } + } + break; + + /* Everything else cannot happen. */ + default: + gcc_unreachable (); + } + } + else + gcc_assert (GET_CODE (addr) == PLUS); + } + if (GET_CODE (addr) == PLUS) + { + rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1); + + gcc_assert (!TLS_SYMBOLIC_CONST (op0)); + gcc_assert (!TLS_SYMBOLIC_CONST (op1)); + + /* Check first to see if this is a constant offset + from a local symbol reference. */ + if ((GET_CODE (op0) == LABEL_REF + || (GET_CODE (op0) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (op0))) + && GET_CODE (op1) == CONST_INT) + { + if (TARGET_CPU_ZARCH + && larl_operand (op0, VOIDmode) + && INTVAL (op1) < (HOST_WIDE_INT)1 << 31 + && INTVAL (op1) >= -((HOST_WIDE_INT)1 << 31)) + { + if (INTVAL (op1) & 1) + { + /* LARL can't handle odd offsets, so emit a + pair of LARL and LA. */ + rtx temp = reg? reg : gen_reg_rtx (Pmode); + + if (!DISP_IN_RANGE (INTVAL (op1))) + { + HOST_WIDE_INT even = INTVAL (op1) - 1; + op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even)); + op0 = gen_rtx_CONST (Pmode, op0); + op1 = const1_rtx; + } + + emit_move_insn (temp, op0); + new_rtx = gen_rtx_PLUS (Pmode, temp, op1); + + if (reg != 0) + { + s390_load_address (reg, new_rtx); + new_rtx = reg; + } + } + else + { + /* If the offset is even, we can just use LARL. + This will happen automatically. */ + } + } + else + { + /* Access local symbols relative to the GOT. */ + + rtx temp = reg? reg : gen_reg_rtx (Pmode); + + if (reload_in_progress || reload_completed) + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); + + addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0), + UNSPEC_GOTOFF); + addr = gen_rtx_PLUS (Pmode, addr, op1); + addr = gen_rtx_CONST (Pmode, addr); + addr = force_const_mem (Pmode, addr); + emit_move_insn (temp, addr); + + new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp); + if (reg != 0) + { + s390_load_address (reg, new_rtx); + new_rtx = reg; + } + } + } + + /* Now, check whether it is a GOT relative symbol plus offset + that was pulled out of the literal pool. Force it back in. */ + + else if (GET_CODE (op0) == UNSPEC + && GET_CODE (op1) == CONST_INT + && XINT (op0, 1) == UNSPEC_GOTOFF) + { + gcc_assert (XVECLEN (op0, 0) == 1); + + new_rtx = force_const_mem (Pmode, orig); + } + + /* Otherwise, compute the sum. */ + else + { + base = legitimize_pic_address (XEXP (addr, 0), reg); + new_rtx = legitimize_pic_address (XEXP (addr, 1), + base == reg ? NULL_RTX : reg); + if (GET_CODE (new_rtx) == CONST_INT) + new_rtx = plus_constant (base, INTVAL (new_rtx)); + else + { + if (GET_CODE (new_rtx) == PLUS && CONSTANT_P (XEXP (new_rtx, 1))) + { + base = gen_rtx_PLUS (Pmode, base, XEXP (new_rtx, 0)); + new_rtx = XEXP (new_rtx, 1); + } + new_rtx = gen_rtx_PLUS (Pmode, base, new_rtx); + } + + if (GET_CODE (new_rtx) == CONST) + new_rtx = XEXP (new_rtx, 0); + new_rtx = force_operand (new_rtx, 0); + } + } + } + return new_rtx; +} + +/* Load the thread pointer into a register. */ + +rtx +s390_get_thread_pointer (void) +{ + rtx tp = gen_reg_rtx (Pmode); + + emit_move_insn (tp, gen_rtx_REG (Pmode, TP_REGNUM)); + mark_reg_pointer (tp, BITS_PER_WORD); + + return tp; +} + +/* Emit a tls call insn. The call target is the SYMBOL_REF stored + in s390_tls_symbol which always refers to __tls_get_offset. + The returned offset is written to RESULT_REG and an USE rtx is + generated for TLS_CALL. */ + +static GTY(()) rtx s390_tls_symbol; + +static void +s390_emit_tls_call_insn (rtx result_reg, rtx tls_call) +{ + rtx insn; + + gcc_assert (flag_pic); + + if (!s390_tls_symbol) + s390_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_offset"); + + insn = s390_emit_call (s390_tls_symbol, tls_call, result_reg, + gen_rtx_REG (Pmode, RETURN_REGNUM)); + + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), result_reg); + RTL_CONST_CALL_P (insn) = 1; +} + +/* ADDR contains a thread-local SYMBOL_REF. Generate code to compute + this (thread-local) address. REG may be used as temporary. */ + +static rtx +legitimize_tls_address (rtx addr, rtx reg) +{ + rtx new_rtx, tls_call, temp, base, r2, insn; + + if (GET_CODE (addr) == SYMBOL_REF) + switch (tls_symbolic_operand (addr)) + { + case TLS_MODEL_GLOBAL_DYNAMIC: + start_sequence (); + r2 = gen_rtx_REG (Pmode, 2); + tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_TLSGD); + new_rtx = gen_rtx_CONST (Pmode, tls_call); + new_rtx = force_const_mem (Pmode, new_rtx); + emit_move_insn (r2, new_rtx); + s390_emit_tls_call_insn (r2, tls_call); + insn = get_insns (); + end_sequence (); + + new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF); + temp = gen_reg_rtx (Pmode); + emit_libcall_block (insn, temp, r2, new_rtx); + + new_rtx = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp); + if (reg != 0) + { + s390_load_address (reg, new_rtx); + new_rtx = reg; + } + break; + + case TLS_MODEL_LOCAL_DYNAMIC: + start_sequence (); + r2 = gen_rtx_REG (Pmode, 2); + tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM); + new_rtx = gen_rtx_CONST (Pmode, tls_call); + new_rtx = force_const_mem (Pmode, new_rtx); + emit_move_insn (r2, new_rtx); + s390_emit_tls_call_insn (r2, tls_call); + insn = get_insns (); + end_sequence (); + + new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM_NTPOFF); + temp = gen_reg_rtx (Pmode); + emit_libcall_block (insn, temp, r2, new_rtx); + + new_rtx = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp); + base = gen_reg_rtx (Pmode); + s390_load_address (base, new_rtx); + + new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_DTPOFF); + new_rtx = gen_rtx_CONST (Pmode, new_rtx); + new_rtx = force_const_mem (Pmode, new_rtx); + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new_rtx); + + new_rtx = gen_rtx_PLUS (Pmode, base, temp); + if (reg != 0) + { + s390_load_address (reg, new_rtx); + new_rtx = reg; + } + break; + + case TLS_MODEL_INITIAL_EXEC: + if (flag_pic == 1) + { + /* Assume GOT offset < 4k. This is handled the same way + in both 31- and 64-bit code. */ + + if (reload_in_progress || reload_completed) + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); + + new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF); + new_rtx = gen_rtx_CONST (Pmode, new_rtx); + new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new_rtx); + new_rtx = gen_const_mem (Pmode, new_rtx); + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new_rtx); + } + else if (TARGET_CPU_ZARCH) + { + /* If the GOT offset might be >= 4k, we determine the position + of the GOT entry via a PC-relative LARL. */ + + new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF); + new_rtx = gen_rtx_CONST (Pmode, new_rtx); + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new_rtx); + + new_rtx = gen_const_mem (Pmode, temp); + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new_rtx); + } + else if (flag_pic) + { + /* If the GOT offset might be >= 4k, we have to load it + from the literal pool. */ + + if (reload_in_progress || reload_completed) + df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); + + new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF); + new_rtx = gen_rtx_CONST (Pmode, new_rtx); + new_rtx = force_const_mem (Pmode, new_rtx); + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new_rtx); + + new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp); + new_rtx = gen_const_mem (Pmode, new_rtx); + + new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new_rtx, addr), UNSPEC_TLS_LOAD); + temp = gen_reg_rtx (Pmode); + emit_insn (gen_rtx_SET (Pmode, temp, new_rtx)); + } + else + { + /* In position-dependent code, load the absolute address of + the GOT entry from the literal pool. */ + + new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF); + new_rtx = gen_rtx_CONST (Pmode, new_rtx); + new_rtx = force_const_mem (Pmode, new_rtx); + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new_rtx); + + new_rtx = temp; + new_rtx = gen_const_mem (Pmode, new_rtx); + new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new_rtx, addr), UNSPEC_TLS_LOAD); + temp = gen_reg_rtx (Pmode); + emit_insn (gen_rtx_SET (Pmode, temp, new_rtx)); + } + + new_rtx = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp); + if (reg != 0) + { + s390_load_address (reg, new_rtx); + new_rtx = reg; + } + break; + + case TLS_MODEL_LOCAL_EXEC: + new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF); + new_rtx = gen_rtx_CONST (Pmode, new_rtx); + new_rtx = force_const_mem (Pmode, new_rtx); + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, new_rtx); + + new_rtx = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp); + if (reg != 0) + { + s390_load_address (reg, new_rtx); + new_rtx = reg; + } + break; + + default: + gcc_unreachable (); + } + + else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == UNSPEC) + { + switch (XINT (XEXP (addr, 0), 1)) + { + case UNSPEC_INDNTPOFF: + gcc_assert (TARGET_CPU_ZARCH); + new_rtx = addr; + break; + + default: + gcc_unreachable (); + } + } + + else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS + && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT) + { + new_rtx = XEXP (XEXP (addr, 0), 0); + if (GET_CODE (new_rtx) != SYMBOL_REF) + new_rtx = gen_rtx_CONST (Pmode, new_rtx); + + new_rtx = legitimize_tls_address (new_rtx, reg); + new_rtx = plus_constant (new_rtx, INTVAL (XEXP (XEXP (addr, 0), 1))); + new_rtx = force_operand (new_rtx, 0); + } + + else + gcc_unreachable (); /* for now ... */ + + return new_rtx; +} + +/* Emit insns making the address in operands[1] valid for a standard + move to operands[0]. operands[1] is replaced by an address which + should be used instead of the former RTX to emit the move + pattern. */ + +void +emit_symbolic_move (rtx *operands) +{ + rtx temp = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode); + + if (GET_CODE (operands[0]) == MEM) + operands[1] = force_reg (Pmode, operands[1]); + else if (TLS_SYMBOLIC_CONST (operands[1])) + operands[1] = legitimize_tls_address (operands[1], temp); + else if (flag_pic) + operands[1] = legitimize_pic_address (operands[1], temp); +} + +/* Try machine-dependent ways of modifying an illegitimate address X + to be legitimate. If we find one, return the new, valid address. + + OLDX is the address as it was before break_out_memory_refs was called. + In some cases it is useful to look at this to decide what needs to be done. + + MODE is the mode of the operand pointed to by X. + + When -fpic is used, special handling is needed for symbolic references. + See comments by legitimize_pic_address for details. */ + +static rtx +s390_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED) +{ + rtx constant_term = const0_rtx; + + if (TLS_SYMBOLIC_CONST (x)) + { + x = legitimize_tls_address (x, 0); + + if (s390_legitimate_address_p (mode, x, FALSE)) + return x; + } + else if (GET_CODE (x) == PLUS + && (TLS_SYMBOLIC_CONST (XEXP (x, 0)) + || TLS_SYMBOLIC_CONST (XEXP (x, 1)))) + { + return x; + } + else if (flag_pic) + { + if (SYMBOLIC_CONST (x) + || (GET_CODE (x) == PLUS + && (SYMBOLIC_CONST (XEXP (x, 0)) + || SYMBOLIC_CONST (XEXP (x, 1))))) + x = legitimize_pic_address (x, 0); + + if (s390_legitimate_address_p (mode, x, FALSE)) + return x; + } + + x = eliminate_constant_term (x, &constant_term); + + /* Optimize loading of large displacements by splitting them + into the multiple of 4K and the rest; this allows the + former to be CSE'd if possible. + + Don't do this if the displacement is added to a register + pointing into the stack frame, as the offsets will + change later anyway. */ + + if (GET_CODE (constant_term) == CONST_INT + && !TARGET_LONG_DISPLACEMENT + && !DISP_IN_RANGE (INTVAL (constant_term)) + && !(REG_P (x) && REGNO_PTR_FRAME_P (REGNO (x)))) + { + HOST_WIDE_INT lower = INTVAL (constant_term) & 0xfff; + HOST_WIDE_INT upper = INTVAL (constant_term) ^ lower; + + rtx temp = gen_reg_rtx (Pmode); + rtx val = force_operand (GEN_INT (upper), temp); + if (val != temp) + emit_move_insn (temp, val); + + x = gen_rtx_PLUS (Pmode, x, temp); + constant_term = GEN_INT (lower); + } + + if (GET_CODE (x) == PLUS) + { + if (GET_CODE (XEXP (x, 0)) == REG) + { + rtx temp = gen_reg_rtx (Pmode); + rtx val = force_operand (XEXP (x, 1), temp); + if (val != temp) + emit_move_insn (temp, val); + + x = gen_rtx_PLUS (Pmode, XEXP (x, 0), temp); + } + + else if (GET_CODE (XEXP (x, 1)) == REG) + { + rtx temp = gen_reg_rtx (Pmode); + rtx val = force_operand (XEXP (x, 0), temp); + if (val != temp) + emit_move_insn (temp, val); + + x = gen_rtx_PLUS (Pmode, temp, XEXP (x, 1)); + } + } + + if (constant_term != const0_rtx) + x = gen_rtx_PLUS (Pmode, x, constant_term); + + return x; +} + +/* Try a machine-dependent way of reloading an illegitimate address AD + operand. If we find one, push the reload and and return the new address. + + MODE is the mode of the enclosing MEM. OPNUM is the operand number + and TYPE is the reload type of the current reload. */ + +rtx +legitimize_reload_address (rtx ad, enum machine_mode mode ATTRIBUTE_UNUSED, + int opnum, int type) +{ + if (!optimize || TARGET_LONG_DISPLACEMENT) + return NULL_RTX; + + if (GET_CODE (ad) == PLUS) + { + rtx tem = simplify_binary_operation (PLUS, Pmode, + XEXP (ad, 0), XEXP (ad, 1)); + if (tem) + ad = tem; + } + + if (GET_CODE (ad) == PLUS + && GET_CODE (XEXP (ad, 0)) == REG + && GET_CODE (XEXP (ad, 1)) == CONST_INT + && !DISP_IN_RANGE (INTVAL (XEXP (ad, 1)))) + { + HOST_WIDE_INT lower = INTVAL (XEXP (ad, 1)) & 0xfff; + HOST_WIDE_INT upper = INTVAL (XEXP (ad, 1)) ^ lower; + rtx cst, tem, new_rtx; + + cst = GEN_INT (upper); + if (!legitimate_reload_constant_p (cst)) + cst = force_const_mem (Pmode, cst); + + tem = gen_rtx_PLUS (Pmode, XEXP (ad, 0), cst); + new_rtx = gen_rtx_PLUS (Pmode, tem, GEN_INT (lower)); + + push_reload (XEXP (tem, 1), 0, &XEXP (tem, 1), 0, + BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, + opnum, (enum reload_type) type); + return new_rtx; + } + + return NULL_RTX; +} + +/* Emit code to move LEN bytes from DST to SRC. */ + +void +s390_expand_movmem (rtx dst, rtx src, rtx len) +{ + if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256) + { + if (INTVAL (len) > 0) + emit_insn (gen_movmem_short (dst, src, GEN_INT (INTVAL (len) - 1))); + } + + else if (TARGET_MVCLE) + { + emit_insn (gen_movmem_long (dst, src, convert_to_mode (Pmode, len, 1))); + } + + else + { + rtx dst_addr, src_addr, count, blocks, temp; + rtx loop_start_label = gen_label_rtx (); + rtx loop_end_label = gen_label_rtx (); + rtx end_label = gen_label_rtx (); + enum machine_mode mode; + + mode = GET_MODE (len); + if (mode == VOIDmode) + mode = Pmode; + + dst_addr = gen_reg_rtx (Pmode); + src_addr = gen_reg_rtx (Pmode); + count = gen_reg_rtx (mode); + blocks = gen_reg_rtx (mode); + + convert_move (count, len, 1); + emit_cmp_and_jump_insns (count, const0_rtx, + EQ, NULL_RTX, mode, 1, end_label); + + emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX)); + emit_move_insn (src_addr, force_operand (XEXP (src, 0), NULL_RTX)); + dst = change_address (dst, VOIDmode, dst_addr); + src = change_address (src, VOIDmode, src_addr); + + temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, + OPTAB_DIRECT); + if (temp != count) + emit_move_insn (count, temp); + + temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, + OPTAB_DIRECT); + if (temp != blocks) + emit_move_insn (blocks, temp); + + emit_cmp_and_jump_insns (blocks, const0_rtx, + EQ, NULL_RTX, mode, 1, loop_end_label); + + emit_label (loop_start_label); + + if (TARGET_Z10 + && (GET_CODE (len) != CONST_INT || INTVAL (len) > 768)) + { + rtx prefetch; + + /* Issue a read prefetch for the +3 cache line. */ + prefetch = gen_prefetch (gen_rtx_PLUS (Pmode, src_addr, GEN_INT (768)), + const0_rtx, const0_rtx); + PREFETCH_SCHEDULE_BARRIER_P (prefetch) = true; + emit_insn (prefetch); + + /* Issue a write prefetch for the +3 cache line. */ + prefetch = gen_prefetch (gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (768)), + const1_rtx, const0_rtx); + PREFETCH_SCHEDULE_BARRIER_P (prefetch) = true; + emit_insn (prefetch); + } + + emit_insn (gen_movmem_short (dst, src, GEN_INT (255))); + s390_load_address (dst_addr, + gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256))); + s390_load_address (src_addr, + gen_rtx_PLUS (Pmode, src_addr, GEN_INT (256))); + + temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, + OPTAB_DIRECT); + if (temp != blocks) + emit_move_insn (blocks, temp); + + emit_cmp_and_jump_insns (blocks, const0_rtx, + EQ, NULL_RTX, mode, 1, loop_end_label); + + emit_jump (loop_start_label); + emit_label (loop_end_label); + + emit_insn (gen_movmem_short (dst, src, + convert_to_mode (Pmode, count, 1))); + emit_label (end_label); + } +} + +/* Emit code to set LEN bytes at DST to VAL. + Make use of clrmem if VAL is zero. */ + +void +s390_expand_setmem (rtx dst, rtx len, rtx val) +{ + if (GET_CODE (len) == CONST_INT && INTVAL (len) == 0) + return; + + gcc_assert (GET_CODE (val) == CONST_INT || GET_MODE (val) == QImode); + + if (GET_CODE (len) == CONST_INT && INTVAL (len) > 0 && INTVAL (len) <= 257) + { + if (val == const0_rtx && INTVAL (len) <= 256) + emit_insn (gen_clrmem_short (dst, GEN_INT (INTVAL (len) - 1))); + else + { + /* Initialize memory by storing the first byte. */ + emit_move_insn (adjust_address (dst, QImode, 0), val); + + if (INTVAL (len) > 1) + { + /* Initiate 1 byte overlap move. + The first byte of DST is propagated through DSTP1. + Prepare a movmem for: DST+1 = DST (length = LEN - 1). + DST is set to size 1 so the rest of the memory location + does not count as source operand. */ + rtx dstp1 = adjust_address (dst, VOIDmode, 1); + set_mem_size (dst, const1_rtx); + + emit_insn (gen_movmem_short (dstp1, dst, + GEN_INT (INTVAL (len) - 2))); + } + } + } + + else if (TARGET_MVCLE) + { + val = force_not_mem (convert_modes (Pmode, QImode, val, 1)); + emit_insn (gen_setmem_long (dst, convert_to_mode (Pmode, len, 1), val)); + } + + else + { + rtx dst_addr, count, blocks, temp, dstp1 = NULL_RTX; + rtx loop_start_label = gen_label_rtx (); + rtx loop_end_label = gen_label_rtx (); + rtx end_label = gen_label_rtx (); + enum machine_mode mode; + + mode = GET_MODE (len); + if (mode == VOIDmode) + mode = Pmode; + + dst_addr = gen_reg_rtx (Pmode); + count = gen_reg_rtx (mode); + blocks = gen_reg_rtx (mode); + + convert_move (count, len, 1); + emit_cmp_and_jump_insns (count, const0_rtx, + EQ, NULL_RTX, mode, 1, end_label); + + emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX)); + dst = change_address (dst, VOIDmode, dst_addr); + + if (val == const0_rtx) + temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, + OPTAB_DIRECT); + else + { + dstp1 = adjust_address (dst, VOIDmode, 1); + set_mem_size (dst, const1_rtx); + + /* Initialize memory by storing the first byte. */ + emit_move_insn (adjust_address (dst, QImode, 0), val); + + /* If count is 1 we are done. */ + emit_cmp_and_jump_insns (count, const1_rtx, + EQ, NULL_RTX, mode, 1, end_label); + + temp = expand_binop (mode, add_optab, count, GEN_INT (-2), count, 1, + OPTAB_DIRECT); + } + if (temp != count) + emit_move_insn (count, temp); + + temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, + OPTAB_DIRECT); + if (temp != blocks) + emit_move_insn (blocks, temp); + + emit_cmp_and_jump_insns (blocks, const0_rtx, + EQ, NULL_RTX, mode, 1, loop_end_label); + + emit_label (loop_start_label); + + if (TARGET_Z10 + && (GET_CODE (len) != CONST_INT || INTVAL (len) > 1024)) + { + /* Issue a write prefetch for the +4 cache line. */ + rtx prefetch = gen_prefetch (gen_rtx_PLUS (Pmode, dst_addr, + GEN_INT (1024)), + const1_rtx, const0_rtx); + emit_insn (prefetch); + PREFETCH_SCHEDULE_BARRIER_P (prefetch) = true; + } + + if (val == const0_rtx) + emit_insn (gen_clrmem_short (dst, GEN_INT (255))); + else + emit_insn (gen_movmem_short (dstp1, dst, GEN_INT (255))); + s390_load_address (dst_addr, + gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256))); + + temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, + OPTAB_DIRECT); + if (temp != blocks) + emit_move_insn (blocks, temp); + + emit_cmp_and_jump_insns (blocks, const0_rtx, + EQ, NULL_RTX, mode, 1, loop_end_label); + + emit_jump (loop_start_label); + emit_label (loop_end_label); + + if (val == const0_rtx) + emit_insn (gen_clrmem_short (dst, convert_to_mode (Pmode, count, 1))); + else + emit_insn (gen_movmem_short (dstp1, dst, convert_to_mode (Pmode, count, 1))); + emit_label (end_label); + } +} + +/* Emit code to compare LEN bytes at OP0 with those at OP1, + and return the result in TARGET. */ + +void +s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len) +{ + rtx ccreg = gen_rtx_REG (CCUmode, CC_REGNUM); + rtx tmp; + + /* As the result of CMPINT is inverted compared to what we need, + we have to swap the operands. */ + tmp = op0; op0 = op1; op1 = tmp; + + if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256) + { + if (INTVAL (len) > 0) + { + emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (INTVAL (len) - 1))); + emit_insn (gen_cmpint (target, ccreg)); + } + else + emit_move_insn (target, const0_rtx); + } + else if (TARGET_MVCLE) + { + emit_insn (gen_cmpmem_long (op0, op1, convert_to_mode (Pmode, len, 1))); + emit_insn (gen_cmpint (target, ccreg)); + } + else + { + rtx addr0, addr1, count, blocks, temp; + rtx loop_start_label = gen_label_rtx (); + rtx loop_end_label = gen_label_rtx (); + rtx end_label = gen_label_rtx (); + enum machine_mode mode; + + mode = GET_MODE (len); + if (mode == VOIDmode) + mode = Pmode; + + addr0 = gen_reg_rtx (Pmode); + addr1 = gen_reg_rtx (Pmode); + count = gen_reg_rtx (mode); + blocks = gen_reg_rtx (mode); + + convert_move (count, len, 1); + emit_cmp_and_jump_insns (count, const0_rtx, + EQ, NULL_RTX, mode, 1, end_label); + + emit_move_insn (addr0, force_operand (XEXP (op0, 0), NULL_RTX)); + emit_move_insn (addr1, force_operand (XEXP (op1, 0), NULL_RTX)); + op0 = change_address (op0, VOIDmode, addr0); + op1 = change_address (op1, VOIDmode, addr1); + + temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, + OPTAB_DIRECT); + if (temp != count) + emit_move_insn (count, temp); + + temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, + OPTAB_DIRECT); + if (temp != blocks) + emit_move_insn (blocks, temp); + + emit_cmp_and_jump_insns (blocks, const0_rtx, + EQ, NULL_RTX, mode, 1, loop_end_label); + + emit_label (loop_start_label); + + if (TARGET_Z10 + && (GET_CODE (len) != CONST_INT || INTVAL (len) > 512)) + { + rtx prefetch; + + /* Issue a read prefetch for the +2 cache line of operand 1. */ + prefetch = gen_prefetch (gen_rtx_PLUS (Pmode, addr0, GEN_INT (512)), + const0_rtx, const0_rtx); + emit_insn (prefetch); + PREFETCH_SCHEDULE_BARRIER_P (prefetch) = true; + + /* Issue a read prefetch for the +2 cache line of operand 2. */ + prefetch = gen_prefetch (gen_rtx_PLUS (Pmode, addr1, GEN_INT (512)), + const0_rtx, const0_rtx); + emit_insn (prefetch); + PREFETCH_SCHEDULE_BARRIER_P (prefetch) = true; + } + + emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (255))); + temp = gen_rtx_NE (VOIDmode, ccreg, const0_rtx); + temp = gen_rtx_IF_THEN_ELSE (VOIDmode, temp, + gen_rtx_LABEL_REF (VOIDmode, end_label), pc_rtx); + temp = gen_rtx_SET (VOIDmode, pc_rtx, temp); + emit_jump_insn (temp); + + s390_load_address (addr0, + gen_rtx_PLUS (Pmode, addr0, GEN_INT (256))); + s390_load_address (addr1, + gen_rtx_PLUS (Pmode, addr1, GEN_INT (256))); + + temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, + OPTAB_DIRECT); + if (temp != blocks) + emit_move_insn (blocks, temp); + + emit_cmp_and_jump_insns (blocks, const0_rtx, + EQ, NULL_RTX, mode, 1, loop_end_label); + + emit_jump (loop_start_label); + emit_label (loop_end_label); + + emit_insn (gen_cmpmem_short (op0, op1, + convert_to_mode (Pmode, count, 1))); + emit_label (end_label); + + emit_insn (gen_cmpint (target, ccreg)); + } +} + + +/* Expand conditional increment or decrement using alc/slb instructions. + Should generate code setting DST to either SRC or SRC + INCREMENT, + depending on the result of the comparison CMP_OP0 CMP_CODE CMP_OP1. + Returns true if successful, false otherwise. + + That makes it possible to implement some if-constructs without jumps e.g.: + (borrow = CC0 | CC1 and carry = CC2 | CC3) + unsigned int a, b, c; + if (a < b) c++; -> CCU b > a -> CC2; c += carry; + if (a < b) c--; -> CCL3 a - b -> borrow; c -= borrow; + if (a <= b) c++; -> CCL3 b - a -> borrow; c += carry; + if (a <= b) c--; -> CCU a <= b -> borrow; c -= borrow; + + Checks for EQ and NE with a nonzero value need an additional xor e.g.: + if (a == b) c++; -> CCL3 a ^= b; 0 - a -> borrow; c += carry; + if (a == b) c--; -> CCU a ^= b; a <= 0 -> CC0 | CC1; c -= borrow; + if (a != b) c++; -> CCU a ^= b; a > 0 -> CC2; c += carry; + if (a != b) c--; -> CCL3 a ^= b; 0 - a -> borrow; c -= borrow; */ + +bool +s390_expand_addcc (enum rtx_code cmp_code, rtx cmp_op0, rtx cmp_op1, + rtx dst, rtx src, rtx increment) +{ + enum machine_mode cmp_mode; + enum machine_mode cc_mode; + rtx op_res; + rtx insn; + rtvec p; + int ret; + + if ((GET_MODE (cmp_op0) == SImode || GET_MODE (cmp_op0) == VOIDmode) + && (GET_MODE (cmp_op1) == SImode || GET_MODE (cmp_op1) == VOIDmode)) + cmp_mode = SImode; + else if ((GET_MODE (cmp_op0) == DImode || GET_MODE (cmp_op0) == VOIDmode) + && (GET_MODE (cmp_op1) == DImode || GET_MODE (cmp_op1) == VOIDmode)) + cmp_mode = DImode; + else + return false; + + /* Try ADD LOGICAL WITH CARRY. */ + if (increment == const1_rtx) + { + /* Determine CC mode to use. */ + if (cmp_code == EQ || cmp_code == NE) + { + if (cmp_op1 != const0_rtx) + { + cmp_op0 = expand_simple_binop (cmp_mode, XOR, cmp_op0, cmp_op1, + NULL_RTX, 0, OPTAB_WIDEN); + cmp_op1 = const0_rtx; + } + + cmp_code = cmp_code == EQ ? LEU : GTU; + } + + if (cmp_code == LTU || cmp_code == LEU) + { + rtx tem = cmp_op0; + cmp_op0 = cmp_op1; + cmp_op1 = tem; + cmp_code = swap_condition (cmp_code); + } + + switch (cmp_code) + { + case GTU: + cc_mode = CCUmode; + break; + + case GEU: + cc_mode = CCL3mode; + break; + + default: + return false; + } + + /* Emit comparison instruction pattern. */ + if (!register_operand (cmp_op0, cmp_mode)) + cmp_op0 = force_reg (cmp_mode, cmp_op0); + + insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (cc_mode, CC_REGNUM), + gen_rtx_COMPARE (cc_mode, cmp_op0, cmp_op1)); + /* We use insn_invalid_p here to add clobbers if required. */ + ret = insn_invalid_p (emit_insn (insn)); + gcc_assert (!ret); + + /* Emit ALC instruction pattern. */ + op_res = gen_rtx_fmt_ee (cmp_code, GET_MODE (dst), + gen_rtx_REG (cc_mode, CC_REGNUM), + const0_rtx); + + if (src != const0_rtx) + { + if (!register_operand (src, GET_MODE (dst))) + src = force_reg (GET_MODE (dst), src); + + op_res = gen_rtx_PLUS (GET_MODE (dst), op_res, src); + op_res = gen_rtx_PLUS (GET_MODE (dst), op_res, const0_rtx); + } + + p = rtvec_alloc (2); + RTVEC_ELT (p, 0) = + gen_rtx_SET (VOIDmode, dst, op_res); + RTVEC_ELT (p, 1) = + gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM)); + emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); + + return true; + } + + /* Try SUBTRACT LOGICAL WITH BORROW. */ + if (increment == constm1_rtx) + { + /* Determine CC mode to use. */ + if (cmp_code == EQ || cmp_code == NE) + { + if (cmp_op1 != const0_rtx) + { + cmp_op0 = expand_simple_binop (cmp_mode, XOR, cmp_op0, cmp_op1, + NULL_RTX, 0, OPTAB_WIDEN); + cmp_op1 = const0_rtx; + } + + cmp_code = cmp_code == EQ ? LEU : GTU; + } + + if (cmp_code == GTU || cmp_code == GEU) + { + rtx tem = cmp_op0; + cmp_op0 = cmp_op1; + cmp_op1 = tem; + cmp_code = swap_condition (cmp_code); + } + + switch (cmp_code) + { + case LEU: + cc_mode = CCUmode; + break; + + case LTU: + cc_mode = CCL3mode; + break; + + default: + return false; + } + + /* Emit comparison instruction pattern. */ + if (!register_operand (cmp_op0, cmp_mode)) + cmp_op0 = force_reg (cmp_mode, cmp_op0); + + insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (cc_mode, CC_REGNUM), + gen_rtx_COMPARE (cc_mode, cmp_op0, cmp_op1)); + /* We use insn_invalid_p here to add clobbers if required. */ + ret = insn_invalid_p (emit_insn (insn)); + gcc_assert (!ret); + + /* Emit SLB instruction pattern. */ + if (!register_operand (src, GET_MODE (dst))) + src = force_reg (GET_MODE (dst), src); + + op_res = gen_rtx_MINUS (GET_MODE (dst), + gen_rtx_MINUS (GET_MODE (dst), src, const0_rtx), + gen_rtx_fmt_ee (cmp_code, GET_MODE (dst), + gen_rtx_REG (cc_mode, CC_REGNUM), + const0_rtx)); + p = rtvec_alloc (2); + RTVEC_ELT (p, 0) = + gen_rtx_SET (VOIDmode, dst, op_res); + RTVEC_ELT (p, 1) = + gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM)); + emit_insn (gen_rtx_PARALLEL (VOIDmode, p)); + + return true; + } + + return false; +} + +/* Expand code for the insv template. Return true if successful. */ + +bool +s390_expand_insv (rtx dest, rtx op1, rtx op2, rtx src) +{ + int bitsize = INTVAL (op1); + int bitpos = INTVAL (op2); + + /* On z10 we can use the risbg instruction to implement insv. */ + if (TARGET_Z10 + && ((GET_MODE (dest) == DImode && GET_MODE (src) == DImode) + || (GET_MODE (dest) == SImode && GET_MODE (src) == SImode))) + { + rtx op; + rtx clobber; + + op = gen_rtx_SET (GET_MODE(src), + gen_rtx_ZERO_EXTRACT (GET_MODE (dest), dest, op1, op2), + src); + clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM)); + emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clobber))); + + return true; + } + + /* We need byte alignment. */ + if (bitsize % BITS_PER_UNIT) + return false; + + if (bitpos == 0 + && memory_operand (dest, VOIDmode) + && (register_operand (src, word_mode) + || const_int_operand (src, VOIDmode))) + { + /* Emit standard pattern if possible. */ + enum machine_mode mode = smallest_mode_for_size (bitsize, MODE_INT); + if (GET_MODE_BITSIZE (mode) == bitsize) + emit_move_insn (adjust_address (dest, mode, 0), gen_lowpart (mode, src)); + + /* (set (ze (mem)) (const_int)). */ + else if (const_int_operand (src, VOIDmode)) + { + int size = bitsize / BITS_PER_UNIT; + rtx src_mem = adjust_address (force_const_mem (word_mode, src), BLKmode, + GET_MODE_SIZE (word_mode) - size); + + dest = adjust_address (dest, BLKmode, 0); + set_mem_size (dest, GEN_INT (size)); + s390_expand_movmem (dest, src_mem, GEN_INT (size)); + } + + /* (set (ze (mem)) (reg)). */ + else if (register_operand (src, word_mode)) + { + if (bitsize <= GET_MODE_BITSIZE (SImode)) + emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, op1, + const0_rtx), src); + else + { + /* Emit st,stcmh sequence. */ + int stcmh_width = bitsize - GET_MODE_BITSIZE (SImode); + int size = stcmh_width / BITS_PER_UNIT; + + emit_move_insn (adjust_address (dest, SImode, size), + gen_lowpart (SImode, src)); + set_mem_size (dest, GEN_INT (size)); + emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, GEN_INT + (stcmh_width), const0_rtx), + gen_rtx_LSHIFTRT (word_mode, src, GEN_INT + (GET_MODE_BITSIZE (SImode)))); + } + } + else + return false; + + return true; + } + + /* (set (ze (reg)) (const_int)). */ + if (TARGET_ZARCH + && register_operand (dest, word_mode) + && (bitpos % 16) == 0 + && (bitsize % 16) == 0 + && const_int_operand (src, VOIDmode)) + { + HOST_WIDE_INT val = INTVAL (src); + int regpos = bitpos + bitsize; + + while (regpos > bitpos) + { + enum machine_mode putmode; + int putsize; + + if (TARGET_EXTIMM && (regpos % 32 == 0) && (regpos >= bitpos + 32)) + putmode = SImode; + else + putmode = HImode; + + putsize = GET_MODE_BITSIZE (putmode); + regpos -= putsize; + emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, + GEN_INT (putsize), + GEN_INT (regpos)), + gen_int_mode (val, putmode)); + val >>= putsize; + } + gcc_assert (regpos == bitpos); + return true; + } + + return false; +} + +/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic which returns a + register that holds VAL of mode MODE shifted by COUNT bits. */ + +static inline rtx +s390_expand_mask_and_shift (rtx val, enum machine_mode mode, rtx count) +{ + val = expand_simple_binop (SImode, AND, val, GEN_INT (GET_MODE_MASK (mode)), + NULL_RTX, 1, OPTAB_DIRECT); + return expand_simple_binop (SImode, ASHIFT, val, count, + NULL_RTX, 1, OPTAB_DIRECT); +} + +/* Structure to hold the initial parameters for a compare_and_swap operation + in HImode and QImode. */ + +struct alignment_context +{ + rtx memsi; /* SI aligned memory location. */ + rtx shift; /* Bit offset with regard to lsb. */ + rtx modemask; /* Mask of the HQImode shifted by SHIFT bits. */ + rtx modemaski; /* ~modemask */ + bool aligned; /* True if memory is aligned, false else. */ +}; + +/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic to initialize + structure AC for transparent simplifying, if the memory alignment is known + to be at least 32bit. MEM is the memory location for the actual operation + and MODE its mode. */ + +static void +init_alignment_context (struct alignment_context *ac, rtx mem, + enum machine_mode mode) +{ + ac->shift = GEN_INT (GET_MODE_SIZE (SImode) - GET_MODE_SIZE (mode)); + ac->aligned = (MEM_ALIGN (mem) >= GET_MODE_BITSIZE (SImode)); + + if (ac->aligned) + ac->memsi = adjust_address (mem, SImode, 0); /* Memory is aligned. */ + else + { + /* Alignment is unknown. */ + rtx byteoffset, addr, align; + + /* Force the address into a register. */ + addr = force_reg (Pmode, XEXP (mem, 0)); + + /* Align it to SImode. */ + align = expand_simple_binop (Pmode, AND, addr, + GEN_INT (-GET_MODE_SIZE (SImode)), + NULL_RTX, 1, OPTAB_DIRECT); + /* Generate MEM. */ + ac->memsi = gen_rtx_MEM (SImode, align); + MEM_VOLATILE_P (ac->memsi) = MEM_VOLATILE_P (mem); + set_mem_alias_set (ac->memsi, ALIAS_SET_MEMORY_BARRIER); + set_mem_align (ac->memsi, GET_MODE_BITSIZE (SImode)); + + /* Calculate shiftcount. */ + byteoffset = expand_simple_binop (Pmode, AND, addr, + GEN_INT (GET_MODE_SIZE (SImode) - 1), + NULL_RTX, 1, OPTAB_DIRECT); + /* As we already have some offset, evaluate the remaining distance. */ + ac->shift = expand_simple_binop (SImode, MINUS, ac->shift, byteoffset, + NULL_RTX, 1, OPTAB_DIRECT); + + } + /* Shift is the byte count, but we need the bitcount. */ + ac->shift = expand_simple_binop (SImode, MULT, ac->shift, GEN_INT (BITS_PER_UNIT), + NULL_RTX, 1, OPTAB_DIRECT); + /* Calculate masks. */ + ac->modemask = expand_simple_binop (SImode, ASHIFT, + GEN_INT (GET_MODE_MASK (mode)), ac->shift, + NULL_RTX, 1, OPTAB_DIRECT); + ac->modemaski = expand_simple_unop (SImode, NOT, ac->modemask, NULL_RTX, 1); +} + +/* Expand an atomic compare and swap operation for HImode and QImode. MEM is + the memory location, CMP the old value to compare MEM with and NEW_RTX the value + to set if CMP == MEM. + CMP is never in memory for compare_and_swap_cc because + expand_bool_compare_and_swap puts it into a register for later compare. */ + +void +s390_expand_cs_hqi (enum machine_mode mode, rtx target, rtx mem, rtx cmp, rtx new_rtx) +{ + struct alignment_context ac; + rtx cmpv, newv, val, resv, cc; + rtx res = gen_reg_rtx (SImode); + rtx csloop = gen_label_rtx (); + rtx csend = gen_label_rtx (); + + gcc_assert (register_operand (target, VOIDmode)); + gcc_assert (MEM_P (mem)); + + init_alignment_context (&ac, mem, mode); + + /* Shift the values to the correct bit positions. */ + if (!(ac.aligned && MEM_P (cmp))) + cmp = s390_expand_mask_and_shift (cmp, mode, ac.shift); + if (!(ac.aligned && MEM_P (new_rtx))) + new_rtx = s390_expand_mask_and_shift (new_rtx, mode, ac.shift); + + /* Load full word. Subsequent loads are performed by CS. */ + val = expand_simple_binop (SImode, AND, ac.memsi, ac.modemaski, + NULL_RTX, 1, OPTAB_DIRECT); + + /* Start CS loop. */ + emit_label (csloop); + /* val = "<mem>00..0<mem>" + * cmp = "00..0<cmp>00..0" + * new = "00..0<new>00..0" + */ + + /* Patch cmp and new with val at correct position. */ + if (ac.aligned && MEM_P (cmp)) + { + cmpv = force_reg (SImode, val); + store_bit_field (cmpv, GET_MODE_BITSIZE (mode), 0, SImode, cmp); + } + else + cmpv = force_reg (SImode, expand_simple_binop (SImode, IOR, cmp, val, + NULL_RTX, 1, OPTAB_DIRECT)); + if (ac.aligned && MEM_P (new_rtx)) + { + newv = force_reg (SImode, val); + store_bit_field (newv, GET_MODE_BITSIZE (mode), 0, SImode, new_rtx); + } + else + newv = force_reg (SImode, expand_simple_binop (SImode, IOR, new_rtx, val, + NULL_RTX, 1, OPTAB_DIRECT)); + + /* Jump to end if we're done (likely?). */ + s390_emit_jump (csend, s390_emit_compare_and_swap (EQ, res, ac.memsi, + cmpv, newv)); + + /* Check for changes outside mode. */ + resv = expand_simple_binop (SImode, AND, res, ac.modemaski, + NULL_RTX, 1, OPTAB_DIRECT); + cc = s390_emit_compare (NE, resv, val); + emit_move_insn (val, resv); + /* Loop internal if so. */ + s390_emit_jump (csloop, cc); + + emit_label (csend); + + /* Return the correct part of the bitfield. */ + convert_move (target, expand_simple_binop (SImode, LSHIFTRT, res, ac.shift, + NULL_RTX, 1, OPTAB_DIRECT), 1); +} + +/* Expand an atomic operation CODE of mode MODE. MEM is the memory location + and VAL the value to play with. If AFTER is true then store the value + MEM holds after the operation, if AFTER is false then store the value MEM + holds before the operation. If TARGET is zero then discard that value, else + store it to TARGET. */ + +void +s390_expand_atomic (enum machine_mode mode, enum rtx_code code, + rtx target, rtx mem, rtx val, bool after) +{ + struct alignment_context ac; + rtx cmp; + rtx new_rtx = gen_reg_rtx (SImode); + rtx orig = gen_reg_rtx (SImode); + rtx csloop = gen_label_rtx (); + + gcc_assert (!target || register_operand (target, VOIDmode)); + gcc_assert (MEM_P (mem)); + + init_alignment_context (&ac, mem, mode); + + /* Shift val to the correct bit positions. + Preserve "icm", but prevent "ex icm". */ + if (!(ac.aligned && code == SET && MEM_P (val))) + val = s390_expand_mask_and_shift (val, mode, ac.shift); + + /* Further preparation insns. */ + if (code == PLUS || code == MINUS) + emit_move_insn (orig, val); + else if (code == MULT || code == AND) /* val = "11..1<val>11..1" */ + val = expand_simple_binop (SImode, XOR, val, ac.modemaski, + NULL_RTX, 1, OPTAB_DIRECT); + + /* Load full word. Subsequent loads are performed by CS. */ + cmp = force_reg (SImode, ac.memsi); + + /* Start CS loop. */ + emit_label (csloop); + emit_move_insn (new_rtx, cmp); + + /* Patch new with val at correct position. */ + switch (code) + { + case PLUS: + case MINUS: + val = expand_simple_binop (SImode, code, new_rtx, orig, + NULL_RTX, 1, OPTAB_DIRECT); + val = expand_simple_binop (SImode, AND, val, ac.modemask, + NULL_RTX, 1, OPTAB_DIRECT); + /* FALLTHRU */ + case SET: + if (ac.aligned && MEM_P (val)) + store_bit_field (new_rtx, GET_MODE_BITSIZE (mode), 0, SImode, val); + else + { + new_rtx = expand_simple_binop (SImode, AND, new_rtx, ac.modemaski, + NULL_RTX, 1, OPTAB_DIRECT); + new_rtx = expand_simple_binop (SImode, IOR, new_rtx, val, + NULL_RTX, 1, OPTAB_DIRECT); + } + break; + case AND: + case IOR: + case XOR: + new_rtx = expand_simple_binop (SImode, code, new_rtx, val, + NULL_RTX, 1, OPTAB_DIRECT); + break; + case MULT: /* NAND */ + new_rtx = expand_simple_binop (SImode, AND, new_rtx, val, + NULL_RTX, 1, OPTAB_DIRECT); + new_rtx = expand_simple_binop (SImode, XOR, new_rtx, ac.modemask, + NULL_RTX, 1, OPTAB_DIRECT); + break; + default: + gcc_unreachable (); + } + + s390_emit_jump (csloop, s390_emit_compare_and_swap (NE, cmp, + ac.memsi, cmp, new_rtx)); + + /* Return the correct part of the bitfield. */ + if (target) + convert_move (target, expand_simple_binop (SImode, LSHIFTRT, + after ? new_rtx : cmp, ac.shift, + NULL_RTX, 1, OPTAB_DIRECT), 1); +} + +/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL. + We need to emit DTP-relative relocations. */ + +static void s390_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED; + +static void +s390_output_dwarf_dtprel (FILE *file, int size, rtx x) +{ + switch (size) + { + case 4: + fputs ("\t.long\t", file); + break; + case 8: + fputs ("\t.quad\t", file); + break; + default: + gcc_unreachable (); + } + output_addr_const (file, x); + fputs ("@DTPOFF", file); +} + +#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING +/* Implement TARGET_MANGLE_TYPE. */ + +static const char * +s390_mangle_type (const_tree type) +{ + if (TYPE_MAIN_VARIANT (type) == long_double_type_node + && TARGET_LONG_DOUBLE_128) + return "g"; + + /* For all other types, use normal C++ mangling. */ + return NULL; +} +#endif + +/* In the name of slightly smaller debug output, and to cater to + general assembler lossage, recognize various UNSPEC sequences + and turn them back into a direct symbol reference. */ + +static rtx +s390_delegitimize_address (rtx orig_x) +{ + rtx x, y; + + orig_x = delegitimize_mem_from_attrs (orig_x); + x = orig_x; + if (GET_CODE (x) != MEM) + return orig_x; + + x = XEXP (x, 0); + if (GET_CODE (x) == PLUS + && GET_CODE (XEXP (x, 1)) == CONST + && GET_CODE (XEXP (x, 0)) == REG + && REGNO (XEXP (x, 0)) == PIC_OFFSET_TABLE_REGNUM) + { + y = XEXP (XEXP (x, 1), 0); + if (GET_CODE (y) == UNSPEC + && XINT (y, 1) == UNSPEC_GOT) + y = XVECEXP (y, 0, 0); + else + return orig_x; + } + else if (GET_CODE (x) == CONST) + { + y = XEXP (x, 0); + if (GET_CODE (y) == UNSPEC + && XINT (y, 1) == UNSPEC_GOTENT) + y = XVECEXP (y, 0, 0); + else + return orig_x; + } + else + return orig_x; + + if (GET_MODE (orig_x) != Pmode) + { + if (GET_MODE (orig_x) == BLKmode) + return orig_x; + y = lowpart_subreg (GET_MODE (orig_x), y, Pmode); + if (y == NULL_RTX) + return orig_x; + } + return y; +} + +/* Output operand OP to stdio stream FILE. + OP is an address (register + offset) which is not used to address data; + instead the rightmost bits are interpreted as the value. */ + +static void +print_shift_count_operand (FILE *file, rtx op) +{ + HOST_WIDE_INT offset; + rtx base; + + /* Extract base register and offset. */ + if (!s390_decompose_shift_count (op, &base, &offset)) + gcc_unreachable (); + + /* Sanity check. */ + if (base) + { + gcc_assert (GET_CODE (base) == REG); + gcc_assert (REGNO (base) < FIRST_PSEUDO_REGISTER); + gcc_assert (REGNO_REG_CLASS (REGNO (base)) == ADDR_REGS); + } + + /* Offsets are constricted to twelve bits. */ + fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset & ((1 << 12) - 1)); + if (base) + fprintf (file, "(%s)", reg_names[REGNO (base)]); +} + +/* See 'get_some_local_dynamic_name'. */ + +static int +get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED) +{ + rtx x = *px; + + if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x)) + { + x = get_pool_constant (x); + return for_each_rtx (&x, get_some_local_dynamic_name_1, 0); + } + + if (GET_CODE (x) == SYMBOL_REF + && tls_symbolic_operand (x) == TLS_MODEL_LOCAL_DYNAMIC) + { + cfun->machine->some_ld_name = XSTR (x, 0); + return 1; + } + + return 0; +} + +/* Locate some local-dynamic symbol still in use by this function + so that we can print its name in local-dynamic base patterns. */ + +static const char * +get_some_local_dynamic_name (void) +{ + rtx insn; + + if (cfun->machine->some_ld_name) + return cfun->machine->some_ld_name; + + for (insn = get_insns (); insn ; insn = NEXT_INSN (insn)) + if (INSN_P (insn) + && for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0)) + return cfun->machine->some_ld_name; + + gcc_unreachable (); +} + +/* Output machine-dependent UNSPECs occurring in address constant X + in assembler syntax to stdio stream FILE. Returns true if the + constant X could be recognized, false otherwise. */ + +static bool +s390_output_addr_const_extra (FILE *file, rtx x) +{ + if (GET_CODE (x) == UNSPEC && XVECLEN (x, 0) == 1) + switch (XINT (x, 1)) + { + case UNSPEC_GOTENT: + output_addr_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@GOTENT"); + return true; + case UNSPEC_GOT: + output_addr_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@GOT"); + return true; + case UNSPEC_GOTOFF: + output_addr_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@GOTOFF"); + return true; + case UNSPEC_PLT: + output_addr_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@PLT"); + return true; + case UNSPEC_PLTOFF: + output_addr_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@PLTOFF"); + return true; + case UNSPEC_TLSGD: + output_addr_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@TLSGD"); + return true; + case UNSPEC_TLSLDM: + assemble_name (file, get_some_local_dynamic_name ()); + fprintf (file, "@TLSLDM"); + return true; + case UNSPEC_DTPOFF: + output_addr_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@DTPOFF"); + return true; + case UNSPEC_NTPOFF: + output_addr_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@NTPOFF"); + return true; + case UNSPEC_GOTNTPOFF: + output_addr_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@GOTNTPOFF"); + return true; + case UNSPEC_INDNTPOFF: + output_addr_const (file, XVECEXP (x, 0, 0)); + fprintf (file, "@INDNTPOFF"); + return true; + } + + if (GET_CODE (x) == UNSPEC && XVECLEN (x, 0) == 2) + switch (XINT (x, 1)) + { + case UNSPEC_POOL_OFFSET: + x = gen_rtx_MINUS (GET_MODE (x), XVECEXP (x, 0, 0), XVECEXP (x, 0, 1)); + output_addr_const (file, x); + return true; + } + return false; +} + +/* Output address operand ADDR in assembler syntax to + stdio stream FILE. */ + +void +print_operand_address (FILE *file, rtx addr) +{ + struct s390_address ad; + + if (s390_symref_operand_p (addr, NULL, NULL)) + { + if (!TARGET_Z10) + { + output_operand_lossage ("symbolic memory references are " + "only supported on z10 or later"); + return; + } + output_addr_const (file, addr); + return; + } + + if (!s390_decompose_address (addr, &ad) + || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base))) + || (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx)))) + output_operand_lossage ("cannot decompose address"); + + if (ad.disp) + output_addr_const (file, ad.disp); + else + fprintf (file, "0"); + + if (ad.base && ad.indx) + fprintf (file, "(%s,%s)", reg_names[REGNO (ad.indx)], + reg_names[REGNO (ad.base)]); + else if (ad.base) + fprintf (file, "(%s)", reg_names[REGNO (ad.base)]); +} + +/* Output operand X in assembler syntax to stdio stream FILE. + CODE specified the format flag. The following format flags + are recognized: + + 'C': print opcode suffix for branch condition. + 'D': print opcode suffix for inverse branch condition. + 'E': print opcode suffix for branch on index instruction. + 'J': print tls_load/tls_gdcall/tls_ldcall suffix + 'G': print the size of the operand in bytes. + 'O': print only the displacement of a memory reference. + 'R': print only the base register of a memory reference. + 'S': print S-type memory reference (base+displacement). + 'N': print the second word of a DImode operand. + 'M': print the second word of a TImode operand. + 'Y': print shift count operand. + + 'b': print integer X as if it's an unsigned byte. + 'c': print integer X as if it's an signed byte. + 'x': print integer X as if it's an unsigned halfword. + 'h': print integer X as if it's a signed halfword. + 'i': print the first nonzero HImode part of X. + 'j': print the first HImode part unequal to -1 of X. + 'k': print the first nonzero SImode part of X. + 'm': print the first SImode part unequal to -1 of X. + 'o': print integer X as if it's an unsigned 32bit word. */ + +void +print_operand (FILE *file, rtx x, int code) +{ + switch (code) + { + case 'C': + fprintf (file, s390_branch_condition_mnemonic (x, FALSE)); + return; + + case 'D': + fprintf (file, s390_branch_condition_mnemonic (x, TRUE)); + return; + + case 'E': + if (GET_CODE (x) == LE) + fprintf (file, "l"); + else if (GET_CODE (x) == GT) + fprintf (file, "h"); + else + output_operand_lossage ("invalid comparison operator " + "for 'E' output modifier"); + return; + + case 'J': + if (GET_CODE (x) == SYMBOL_REF) + { + fprintf (file, "%s", ":tls_load:"); + output_addr_const (file, x); + } + else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD) + { + fprintf (file, "%s", ":tls_gdcall:"); + output_addr_const (file, XVECEXP (x, 0, 0)); + } + else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSLDM) + { + fprintf (file, "%s", ":tls_ldcall:"); + assemble_name (file, get_some_local_dynamic_name ()); + } + else + output_operand_lossage ("invalid reference for 'J' output modifier"); + return; + + case 'G': + fprintf (file, "%u", GET_MODE_SIZE (GET_MODE (x))); + return; + + case 'O': + { + struct s390_address ad; + int ret; + + if (!MEM_P (x)) + { + output_operand_lossage ("memory reference expected for " + "'O' output modifier"); + return; + } + + ret = s390_decompose_address (XEXP (x, 0), &ad); + + if (!ret + || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base))) + || ad.indx) + { + output_operand_lossage ("invalid address for 'O' output modifier"); + return; + } + + if (ad.disp) + output_addr_const (file, ad.disp); + else + fprintf (file, "0"); + } + return; + + case 'R': + { + struct s390_address ad; + int ret; + + if (!MEM_P (x)) + { + output_operand_lossage ("memory reference expected for " + "'R' output modifier"); + return; + } + + ret = s390_decompose_address (XEXP (x, 0), &ad); + + if (!ret + || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base))) + || ad.indx) + { + output_operand_lossage ("invalid address for 'R' output modifier"); + return; + } + + if (ad.base) + fprintf (file, "%s", reg_names[REGNO (ad.base)]); + else + fprintf (file, "0"); + } + return; + + case 'S': + { + struct s390_address ad; + int ret; + + if (!MEM_P (x)) + { + output_operand_lossage ("memory reference expected for " + "'S' output modifier"); + return; + } + ret = s390_decompose_address (XEXP (x, 0), &ad); + + if (!ret + || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base))) + || ad.indx) + { + output_operand_lossage ("invalid address for 'S' output modifier"); + return; + } + + if (ad.disp) + output_addr_const (file, ad.disp); + else + fprintf (file, "0"); + + if (ad.base) + fprintf (file, "(%s)", reg_names[REGNO (ad.base)]); + } + return; + + case 'N': + if (GET_CODE (x) == REG) + x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1); + else if (GET_CODE (x) == MEM) + x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 4)); + else + output_operand_lossage ("register or memory expression expected " + "for 'N' output modifier"); + break; + + case 'M': + if (GET_CODE (x) == REG) + x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1); + else if (GET_CODE (x) == MEM) + x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 8)); + else + output_operand_lossage ("register or memory expression expected " + "for 'M' output modifier"); + break; + + case 'Y': + print_shift_count_operand (file, x); + return; + } + + switch (GET_CODE (x)) + { + case REG: + fprintf (file, "%s", reg_names[REGNO (x)]); + break; + + case MEM: + output_address (XEXP (x, 0)); + break; + + case CONST: + case CODE_LABEL: + case LABEL_REF: + case SYMBOL_REF: + output_addr_const (file, x); + break; + + case CONST_INT: + if (code == 'b') + fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xff); + else if (code == 'c') + fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((INTVAL (x) & 0xff) ^ 0x80) - 0x80); + else if (code == 'x') + fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffff); + else if (code == 'h') + fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((INTVAL (x) & 0xffff) ^ 0x8000) - 0x8000); + else if (code == 'i') + fprintf (file, HOST_WIDE_INT_PRINT_DEC, + s390_extract_part (x, HImode, 0)); + else if (code == 'j') + fprintf (file, HOST_WIDE_INT_PRINT_DEC, + s390_extract_part (x, HImode, -1)); + else if (code == 'k') + fprintf (file, HOST_WIDE_INT_PRINT_DEC, + s390_extract_part (x, SImode, 0)); + else if (code == 'm') + fprintf (file, HOST_WIDE_INT_PRINT_DEC, + s390_extract_part (x, SImode, -1)); + else if (code == 'o') + fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffffffff); + else + fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x)); + break; + + case CONST_DOUBLE: + gcc_assert (GET_MODE (x) == VOIDmode); + if (code == 'b') + fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xff); + else if (code == 'x') + fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xffff); + else if (code == 'h') + fprintf (file, HOST_WIDE_INT_PRINT_DEC, + ((CONST_DOUBLE_LOW (x) & 0xffff) ^ 0x8000) - 0x8000); + else + { + if (code == 0) + output_operand_lossage ("invalid constant - try using " + "an output modifier"); + else + output_operand_lossage ("invalid constant for output modifier '%c'", + code); + } + break; + + default: + if (code == 0) + output_operand_lossage ("invalid expression - try using " + "an output modifier"); + else + output_operand_lossage ("invalid expression for output " + "modifier '%c'", code); + break; + } +} + +/* Target hook for assembling integer objects. We need to define it + here to work a round a bug in some versions of GAS, which couldn't + handle values smaller than INT_MIN when printed in decimal. */ + +static bool +s390_assemble_integer (rtx x, unsigned int size, int aligned_p) +{ + if (size == 8 && aligned_p + && GET_CODE (x) == CONST_INT && INTVAL (x) < INT_MIN) + { + fprintf (asm_out_file, "\t.quad\t" HOST_WIDE_INT_PRINT_HEX "\n", + INTVAL (x)); + return true; + } + return default_assemble_integer (x, size, aligned_p); +} + +/* Returns true if register REGNO is used for forming + a memory address in expression X. */ + +static bool +reg_used_in_mem_p (int regno, rtx x) +{ + enum rtx_code code = GET_CODE (x); + int i, j; + const char *fmt; + + if (code == MEM) + { + if (refers_to_regno_p (regno, regno+1, + XEXP (x, 0), 0)) + return true; + } + else if (code == SET + && GET_CODE (SET_DEST (x)) == PC) + { + if (refers_to_regno_p (regno, regno+1, + SET_SRC (x), 0)) + return true; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'e' + && reg_used_in_mem_p (regno, XEXP (x, i))) + return true; + + else if (fmt[i] == 'E') + for (j = 0; j < XVECLEN (x, i); j++) + if (reg_used_in_mem_p (regno, XVECEXP (x, i, j))) + return true; + } + return false; +} + +/* Returns true if expression DEP_RTX sets an address register + used by instruction INSN to address memory. */ + +static bool +addr_generation_dependency_p (rtx dep_rtx, rtx insn) +{ + rtx target, pat; + + if (GET_CODE (dep_rtx) == INSN) + dep_rtx = PATTERN (dep_rtx); + + if (GET_CODE (dep_rtx) == SET) + { + target = SET_DEST (dep_rtx); + if (GET_CODE (target) == STRICT_LOW_PART) + target = XEXP (target, 0); + while (GET_CODE (target) == SUBREG) + target = SUBREG_REG (target); + + if (GET_CODE (target) == REG) + { + int regno = REGNO (target); + + if (s390_safe_attr_type (insn) == TYPE_LA) + { + pat = PATTERN (insn); + if (GET_CODE (pat) == PARALLEL) + { + gcc_assert (XVECLEN (pat, 0) == 2); + pat = XVECEXP (pat, 0, 0); + } + gcc_assert (GET_CODE (pat) == SET); + return refers_to_regno_p (regno, regno+1, SET_SRC (pat), 0); + } + else if (get_attr_atype (insn) == ATYPE_AGEN) + return reg_used_in_mem_p (regno, PATTERN (insn)); + } + } + return false; +} + +/* Return 1, if dep_insn sets register used in insn in the agen unit. */ + +int +s390_agen_dep_p (rtx dep_insn, rtx insn) +{ + rtx dep_rtx = PATTERN (dep_insn); + int i; + + if (GET_CODE (dep_rtx) == SET + && addr_generation_dependency_p (dep_rtx, insn)) + return 1; + else if (GET_CODE (dep_rtx) == PARALLEL) + { + for (i = 0; i < XVECLEN (dep_rtx, 0); i++) + { + if (addr_generation_dependency_p (XVECEXP (dep_rtx, 0, i), insn)) + return 1; + } + } + return 0; +} + + +/* A C statement (sans semicolon) to update the integer scheduling priority + INSN_PRIORITY (INSN). Increase the priority to execute the INSN earlier, + reduce the priority to execute INSN later. Do not define this macro if + you do not need to adjust the scheduling priorities of insns. + + A STD instruction should be scheduled earlier, + in order to use the bypass. */ +static int +s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority) +{ + if (! INSN_P (insn)) + return priority; + + if (s390_tune != PROCESSOR_2084_Z990 + && s390_tune != PROCESSOR_2094_Z9_109 + && s390_tune != PROCESSOR_2097_Z10 + && s390_tune != PROCESSOR_2817_Z196) + return priority; + + switch (s390_safe_attr_type (insn)) + { + case TYPE_FSTOREDF: + case TYPE_FSTORESF: + priority = priority << 3; + break; + case TYPE_STORE: + case TYPE_STM: + priority = priority << 1; + break; + default: + break; + } + return priority; +} + + +/* The number of instructions that can be issued per cycle. */ + +static int +s390_issue_rate (void) +{ + switch (s390_tune) + { + case PROCESSOR_2084_Z990: + case PROCESSOR_2094_Z9_109: + case PROCESSOR_2817_Z196: + return 3; + case PROCESSOR_2097_Z10: + return 2; + default: + return 1; + } +} + +static int +s390_first_cycle_multipass_dfa_lookahead (void) +{ + return 4; +} + +/* Annotate every literal pool reference in X by an UNSPEC_LTREF expression. + Fix up MEMs as required. */ + +static void +annotate_constant_pool_refs (rtx *x) +{ + int i, j; + const char *fmt; + + gcc_assert (GET_CODE (*x) != SYMBOL_REF + || !CONSTANT_POOL_ADDRESS_P (*x)); + + /* Literal pool references can only occur inside a MEM ... */ + if (GET_CODE (*x) == MEM) + { + rtx memref = XEXP (*x, 0); + + if (GET_CODE (memref) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (memref)) + { + rtx base = cfun->machine->base_reg; + rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, memref, base), + UNSPEC_LTREF); + + *x = replace_equiv_address (*x, addr); + return; + } + + if (GET_CODE (memref) == CONST + && GET_CODE (XEXP (memref, 0)) == PLUS + && GET_CODE (XEXP (XEXP (memref, 0), 1)) == CONST_INT + && GET_CODE (XEXP (XEXP (memref, 0), 0)) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (memref, 0), 0))) + { + HOST_WIDE_INT off = INTVAL (XEXP (XEXP (memref, 0), 1)); + rtx sym = XEXP (XEXP (memref, 0), 0); + rtx base = cfun->machine->base_reg; + rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, sym, base), + UNSPEC_LTREF); + + *x = replace_equiv_address (*x, plus_constant (addr, off)); + return; + } + } + + /* ... or a load-address type pattern. */ + if (GET_CODE (*x) == SET) + { + rtx addrref = SET_SRC (*x); + + if (GET_CODE (addrref) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (addrref)) + { + rtx base = cfun->machine->base_reg; + rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, addrref, base), + UNSPEC_LTREF); + + SET_SRC (*x) = addr; + return; + } + + if (GET_CODE (addrref) == CONST + && GET_CODE (XEXP (addrref, 0)) == PLUS + && GET_CODE (XEXP (XEXP (addrref, 0), 1)) == CONST_INT + && GET_CODE (XEXP (XEXP (addrref, 0), 0)) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (addrref, 0), 0))) + { + HOST_WIDE_INT off = INTVAL (XEXP (XEXP (addrref, 0), 1)); + rtx sym = XEXP (XEXP (addrref, 0), 0); + rtx base = cfun->machine->base_reg; + rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, sym, base), + UNSPEC_LTREF); + + SET_SRC (*x) = plus_constant (addr, off); + return; + } + } + + /* Annotate LTREL_BASE as well. */ + if (GET_CODE (*x) == UNSPEC + && XINT (*x, 1) == UNSPEC_LTREL_BASE) + { + rtx base = cfun->machine->base_reg; + *x = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XVECEXP (*x, 0, 0), base), + UNSPEC_LTREL_BASE); + return; + } + + fmt = GET_RTX_FORMAT (GET_CODE (*x)); + for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + annotate_constant_pool_refs (&XEXP (*x, i)); + } + else if (fmt[i] == 'E') + { + for (j = 0; j < XVECLEN (*x, i); j++) + annotate_constant_pool_refs (&XVECEXP (*x, i, j)); + } + } +} + +/* Split all branches that exceed the maximum distance. + Returns true if this created a new literal pool entry. */ + +static int +s390_split_branches (void) +{ + rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); + int new_literal = 0, ret; + rtx insn, pat, tmp, target; + rtx *label; + + /* We need correct insn addresses. */ + + shorten_branches (get_insns ()); + + /* Find all branches that exceed 64KB, and split them. */ + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) != JUMP_INSN) + continue; + + pat = PATTERN (insn); + if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2) + pat = XVECEXP (pat, 0, 0); + if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx) + continue; + + if (GET_CODE (SET_SRC (pat)) == LABEL_REF) + { + label = &SET_SRC (pat); + } + else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) + { + if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) + label = &XEXP (SET_SRC (pat), 1); + else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF) + label = &XEXP (SET_SRC (pat), 2); + else + continue; + } + else + continue; + + if (get_attr_length (insn) <= 4) + continue; + + /* We are going to use the return register as scratch register, + make sure it will be saved/restored by the prologue/epilogue. */ + cfun_frame_layout.save_return_addr_p = 1; + + if (!flag_pic) + { + new_literal = 1; + tmp = force_const_mem (Pmode, *label); + tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn); + INSN_ADDRESSES_NEW (tmp, -1); + annotate_constant_pool_refs (&PATTERN (tmp)); + + target = temp_reg; + } + else + { + new_literal = 1; + target = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, *label), + UNSPEC_LTREL_OFFSET); + target = gen_rtx_CONST (Pmode, target); + target = force_const_mem (Pmode, target); + tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn); + INSN_ADDRESSES_NEW (tmp, -1); + annotate_constant_pool_refs (&PATTERN (tmp)); + + target = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XEXP (target, 0), + cfun->machine->base_reg), + UNSPEC_LTREL_BASE); + target = gen_rtx_PLUS (Pmode, temp_reg, target); + } + + ret = validate_change (insn, label, target, 0); + gcc_assert (ret); + } + + return new_literal; +} + + +/* Find an annotated literal pool symbol referenced in RTX X, + and store it at REF. Will abort if X contains references to + more than one such pool symbol; multiple references to the same + symbol are allowed, however. + + The rtx pointed to by REF must be initialized to NULL_RTX + by the caller before calling this routine. */ + +static void +find_constant_pool_ref (rtx x, rtx *ref) +{ + int i, j; + const char *fmt; + + /* Ignore LTREL_BASE references. */ + if (GET_CODE (x) == UNSPEC + && XINT (x, 1) == UNSPEC_LTREL_BASE) + return; + /* Likewise POOL_ENTRY insns. */ + if (GET_CODE (x) == UNSPEC_VOLATILE + && XINT (x, 1) == UNSPECV_POOL_ENTRY) + return; + + gcc_assert (GET_CODE (x) != SYMBOL_REF + || !CONSTANT_POOL_ADDRESS_P (x)); + + if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_LTREF) + { + rtx sym = XVECEXP (x, 0, 0); + gcc_assert (GET_CODE (sym) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (sym)); + + if (*ref == NULL_RTX) + *ref = sym; + else + gcc_assert (*ref == sym); + + return; + } + + fmt = GET_RTX_FORMAT (GET_CODE (x)); + for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + find_constant_pool_ref (XEXP (x, i), ref); + } + else if (fmt[i] == 'E') + { + for (j = 0; j < XVECLEN (x, i); j++) + find_constant_pool_ref (XVECEXP (x, i, j), ref); + } + } +} + +/* Replace every reference to the annotated literal pool + symbol REF in X by its base plus OFFSET. */ + +static void +replace_constant_pool_ref (rtx *x, rtx ref, rtx offset) +{ + int i, j; + const char *fmt; + + gcc_assert (*x != ref); + + if (GET_CODE (*x) == UNSPEC + && XINT (*x, 1) == UNSPEC_LTREF + && XVECEXP (*x, 0, 0) == ref) + { + *x = gen_rtx_PLUS (Pmode, XVECEXP (*x, 0, 1), offset); + return; + } + + if (GET_CODE (*x) == PLUS + && GET_CODE (XEXP (*x, 1)) == CONST_INT + && GET_CODE (XEXP (*x, 0)) == UNSPEC + && XINT (XEXP (*x, 0), 1) == UNSPEC_LTREF + && XVECEXP (XEXP (*x, 0), 0, 0) == ref) + { + rtx addr = gen_rtx_PLUS (Pmode, XVECEXP (XEXP (*x, 0), 0, 1), offset); + *x = plus_constant (addr, INTVAL (XEXP (*x, 1))); + return; + } + + fmt = GET_RTX_FORMAT (GET_CODE (*x)); + for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + replace_constant_pool_ref (&XEXP (*x, i), ref, offset); + } + else if (fmt[i] == 'E') + { + for (j = 0; j < XVECLEN (*x, i); j++) + replace_constant_pool_ref (&XVECEXP (*x, i, j), ref, offset); + } + } +} + +/* Check whether X contains an UNSPEC_LTREL_BASE. + Return its constant pool symbol if found, NULL_RTX otherwise. */ + +static rtx +find_ltrel_base (rtx x) +{ + int i, j; + const char *fmt; + + if (GET_CODE (x) == UNSPEC + && XINT (x, 1) == UNSPEC_LTREL_BASE) + return XVECEXP (x, 0, 0); + + fmt = GET_RTX_FORMAT (GET_CODE (x)); + for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + rtx fnd = find_ltrel_base (XEXP (x, i)); + if (fnd) + return fnd; + } + else if (fmt[i] == 'E') + { + for (j = 0; j < XVECLEN (x, i); j++) + { + rtx fnd = find_ltrel_base (XVECEXP (x, i, j)); + if (fnd) + return fnd; + } + } + } + + return NULL_RTX; +} + +/* Replace any occurrence of UNSPEC_LTREL_BASE in X with its base. */ + +static void +replace_ltrel_base (rtx *x) +{ + int i, j; + const char *fmt; + + if (GET_CODE (*x) == UNSPEC + && XINT (*x, 1) == UNSPEC_LTREL_BASE) + { + *x = XVECEXP (*x, 0, 1); + return; + } + + fmt = GET_RTX_FORMAT (GET_CODE (*x)); + for (i = GET_RTX_LENGTH (GET_CODE (*x)) - 1; i >= 0; i--) + { + if (fmt[i] == 'e') + { + replace_ltrel_base (&XEXP (*x, i)); + } + else if (fmt[i] == 'E') + { + for (j = 0; j < XVECLEN (*x, i); j++) + replace_ltrel_base (&XVECEXP (*x, i, j)); + } + } +} + + +/* We keep a list of constants which we have to add to internal + constant tables in the middle of large functions. */ + +#define NR_C_MODES 11 +enum machine_mode constant_modes[NR_C_MODES] = +{ + TFmode, TImode, TDmode, + DFmode, DImode, DDmode, + SFmode, SImode, SDmode, + HImode, + QImode +}; + +struct constant +{ + struct constant *next; + rtx value; + rtx label; +}; + +struct constant_pool +{ + struct constant_pool *next; + rtx first_insn; + rtx pool_insn; + bitmap insns; + rtx emit_pool_after; + + struct constant *constants[NR_C_MODES]; + struct constant *execute; + rtx label; + int size; +}; + +/* Allocate new constant_pool structure. */ + +static struct constant_pool * +s390_alloc_pool (void) +{ + struct constant_pool *pool; + int i; + + pool = (struct constant_pool *) xmalloc (sizeof *pool); + pool->next = NULL; + for (i = 0; i < NR_C_MODES; i++) + pool->constants[i] = NULL; + + pool->execute = NULL; + pool->label = gen_label_rtx (); + pool->first_insn = NULL_RTX; + pool->pool_insn = NULL_RTX; + pool->insns = BITMAP_ALLOC (NULL); + pool->size = 0; + pool->emit_pool_after = NULL_RTX; + + return pool; +} + +/* Create new constant pool covering instructions starting at INSN + and chain it to the end of POOL_LIST. */ + +static struct constant_pool * +s390_start_pool (struct constant_pool **pool_list, rtx insn) +{ + struct constant_pool *pool, **prev; + + pool = s390_alloc_pool (); + pool->first_insn = insn; + + for (prev = pool_list; *prev; prev = &(*prev)->next) + ; + *prev = pool; + + return pool; +} + +/* End range of instructions covered by POOL at INSN and emit + placeholder insn representing the pool. */ + +static void +s390_end_pool (struct constant_pool *pool, rtx insn) +{ + rtx pool_size = GEN_INT (pool->size + 8 /* alignment slop */); + + if (!insn) + insn = get_last_insn (); + + pool->pool_insn = emit_insn_after (gen_pool (pool_size), insn); + INSN_ADDRESSES_NEW (pool->pool_insn, -1); +} + +/* Add INSN to the list of insns covered by POOL. */ + +static void +s390_add_pool_insn (struct constant_pool *pool, rtx insn) +{ + bitmap_set_bit (pool->insns, INSN_UID (insn)); +} + +/* Return pool out of POOL_LIST that covers INSN. */ + +static struct constant_pool * +s390_find_pool (struct constant_pool *pool_list, rtx insn) +{ + struct constant_pool *pool; + + for (pool = pool_list; pool; pool = pool->next) + if (bitmap_bit_p (pool->insns, INSN_UID (insn))) + break; + + return pool; +} + +/* Add constant VAL of mode MODE to the constant pool POOL. */ + +static void +s390_add_constant (struct constant_pool *pool, rtx val, enum machine_mode mode) +{ + struct constant *c; + int i; + + for (i = 0; i < NR_C_MODES; i++) + if (constant_modes[i] == mode) + break; + gcc_assert (i != NR_C_MODES); + + for (c = pool->constants[i]; c != NULL; c = c->next) + if (rtx_equal_p (val, c->value)) + break; + + if (c == NULL) + { + c = (struct constant *) xmalloc (sizeof *c); + c->value = val; + c->label = gen_label_rtx (); + c->next = pool->constants[i]; + pool->constants[i] = c; + pool->size += GET_MODE_SIZE (mode); + } +} + +/* Return an rtx that represents the offset of X from the start of + pool POOL. */ + +static rtx +s390_pool_offset (struct constant_pool *pool, rtx x) +{ + rtx label; + + label = gen_rtx_LABEL_REF (GET_MODE (x), pool->label); + x = gen_rtx_UNSPEC (GET_MODE (x), gen_rtvec (2, x, label), + UNSPEC_POOL_OFFSET); + return gen_rtx_CONST (GET_MODE (x), x); +} + +/* Find constant VAL of mode MODE in the constant pool POOL. + Return an RTX describing the distance from the start of + the pool to the location of the new constant. */ + +static rtx +s390_find_constant (struct constant_pool *pool, rtx val, + enum machine_mode mode) +{ + struct constant *c; + int i; + + for (i = 0; i < NR_C_MODES; i++) + if (constant_modes[i] == mode) + break; + gcc_assert (i != NR_C_MODES); + + for (c = pool->constants[i]; c != NULL; c = c->next) + if (rtx_equal_p (val, c->value)) + break; + + gcc_assert (c); + + return s390_pool_offset (pool, gen_rtx_LABEL_REF (Pmode, c->label)); +} + +/* Check whether INSN is an execute. Return the label_ref to its + execute target template if so, NULL_RTX otherwise. */ + +static rtx +s390_execute_label (rtx insn) +{ + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == PARALLEL + && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC + && XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE) + return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2); + + return NULL_RTX; +} + +/* Add execute target for INSN to the constant pool POOL. */ + +static void +s390_add_execute (struct constant_pool *pool, rtx insn) +{ + struct constant *c; + + for (c = pool->execute; c != NULL; c = c->next) + if (INSN_UID (insn) == INSN_UID (c->value)) + break; + + if (c == NULL) + { + c = (struct constant *) xmalloc (sizeof *c); + c->value = insn; + c->label = gen_label_rtx (); + c->next = pool->execute; + pool->execute = c; + pool->size += 6; + } +} + +/* Find execute target for INSN in the constant pool POOL. + Return an RTX describing the distance from the start of + the pool to the location of the execute target. */ + +static rtx +s390_find_execute (struct constant_pool *pool, rtx insn) +{ + struct constant *c; + + for (c = pool->execute; c != NULL; c = c->next) + if (INSN_UID (insn) == INSN_UID (c->value)) + break; + + gcc_assert (c); + + return s390_pool_offset (pool, gen_rtx_LABEL_REF (Pmode, c->label)); +} + +/* For an execute INSN, extract the execute target template. */ + +static rtx +s390_execute_target (rtx insn) +{ + rtx pattern = PATTERN (insn); + gcc_assert (s390_execute_label (insn)); + + if (XVECLEN (pattern, 0) == 2) + { + pattern = copy_rtx (XVECEXP (pattern, 0, 1)); + } + else + { + rtvec vec = rtvec_alloc (XVECLEN (pattern, 0) - 1); + int i; + + for (i = 0; i < XVECLEN (pattern, 0) - 1; i++) + RTVEC_ELT (vec, i) = copy_rtx (XVECEXP (pattern, 0, i + 1)); + + pattern = gen_rtx_PARALLEL (VOIDmode, vec); + } + + return pattern; +} + +/* Indicate that INSN cannot be duplicated. This is the case for + execute insns that carry a unique label. */ + +static bool +s390_cannot_copy_insn_p (rtx insn) +{ + rtx label = s390_execute_label (insn); + return label && label != const0_rtx; +} + +/* Dump out the constants in POOL. If REMOTE_LABEL is true, + do not emit the pool base label. */ + +static void +s390_dump_pool (struct constant_pool *pool, bool remote_label) +{ + struct constant *c; + rtx insn = pool->pool_insn; + int i; + + /* Switch to rodata section. */ + if (TARGET_CPU_ZARCH) + { + insn = emit_insn_after (gen_pool_section_start (), insn); + INSN_ADDRESSES_NEW (insn, -1); + } + + /* Ensure minimum pool alignment. */ + if (TARGET_CPU_ZARCH) + insn = emit_insn_after (gen_pool_align (GEN_INT (8)), insn); + else + insn = emit_insn_after (gen_pool_align (GEN_INT (4)), insn); + INSN_ADDRESSES_NEW (insn, -1); + + /* Emit pool base label. */ + if (!remote_label) + { + insn = emit_label_after (pool->label, insn); + INSN_ADDRESSES_NEW (insn, -1); + } + + /* Dump constants in descending alignment requirement order, + ensuring proper alignment for every constant. */ + for (i = 0; i < NR_C_MODES; i++) + for (c = pool->constants[i]; c; c = c->next) + { + /* Convert UNSPEC_LTREL_OFFSET unspecs to pool-relative references. */ + rtx value = copy_rtx (c->value); + if (GET_CODE (value) == CONST + && GET_CODE (XEXP (value, 0)) == UNSPEC + && XINT (XEXP (value, 0), 1) == UNSPEC_LTREL_OFFSET + && XVECLEN (XEXP (value, 0), 0) == 1) + value = s390_pool_offset (pool, XVECEXP (XEXP (value, 0), 0, 0)); + + insn = emit_label_after (c->label, insn); + INSN_ADDRESSES_NEW (insn, -1); + + value = gen_rtx_UNSPEC_VOLATILE (constant_modes[i], + gen_rtvec (1, value), + UNSPECV_POOL_ENTRY); + insn = emit_insn_after (value, insn); + INSN_ADDRESSES_NEW (insn, -1); + } + + /* Ensure minimum alignment for instructions. */ + insn = emit_insn_after (gen_pool_align (GEN_INT (2)), insn); + INSN_ADDRESSES_NEW (insn, -1); + + /* Output in-pool execute template insns. */ + for (c = pool->execute; c; c = c->next) + { + insn = emit_label_after (c->label, insn); + INSN_ADDRESSES_NEW (insn, -1); + + insn = emit_insn_after (s390_execute_target (c->value), insn); + INSN_ADDRESSES_NEW (insn, -1); + } + + /* Switch back to previous section. */ + if (TARGET_CPU_ZARCH) + { + insn = emit_insn_after (gen_pool_section_end (), insn); + INSN_ADDRESSES_NEW (insn, -1); + } + + insn = emit_barrier_after (insn); + INSN_ADDRESSES_NEW (insn, -1); + + /* Remove placeholder insn. */ + remove_insn (pool->pool_insn); +} + +/* Free all memory used by POOL. */ + +static void +s390_free_pool (struct constant_pool *pool) +{ + struct constant *c, *next; + int i; + + for (i = 0; i < NR_C_MODES; i++) + for (c = pool->constants[i]; c; c = next) + { + next = c->next; + free (c); + } + + for (c = pool->execute; c; c = next) + { + next = c->next; + free (c); + } + + BITMAP_FREE (pool->insns); + free (pool); +} + + +/* Collect main literal pool. Return NULL on overflow. */ + +static struct constant_pool * +s390_mainpool_start (void) +{ + struct constant_pool *pool; + rtx insn; + + pool = s390_alloc_pool (); + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC_VOLATILE + && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPECV_MAIN_POOL) + { + gcc_assert (!pool->pool_insn); + pool->pool_insn = insn; + } + + if (!TARGET_CPU_ZARCH && s390_execute_label (insn)) + { + s390_add_execute (pool, insn); + } + else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) + { + rtx pool_ref = NULL_RTX; + find_constant_pool_ref (PATTERN (insn), &pool_ref); + if (pool_ref) + { + rtx constant = get_pool_constant (pool_ref); + enum machine_mode mode = get_pool_mode (pool_ref); + s390_add_constant (pool, constant, mode); + } + } + + /* If hot/cold partitioning is enabled we have to make sure that + the literal pool is emitted in the same section where the + initialization of the literal pool base pointer takes place. + emit_pool_after is only used in the non-overflow case on non + Z cpus where we can emit the literal pool at the end of the + function body within the text section. */ + if (NOTE_P (insn) + && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS + && !pool->emit_pool_after) + pool->emit_pool_after = PREV_INSN (insn); + } + + gcc_assert (pool->pool_insn || pool->size == 0); + + if (pool->size >= 4096) + { + /* We're going to chunkify the pool, so remove the main + pool placeholder insn. */ + remove_insn (pool->pool_insn); + + s390_free_pool (pool); + pool = NULL; + } + + /* If the functions ends with the section where the literal pool + should be emitted set the marker to its end. */ + if (pool && !pool->emit_pool_after) + pool->emit_pool_after = get_last_insn (); + + return pool; +} + +/* POOL holds the main literal pool as collected by s390_mainpool_start. + Modify the current function to output the pool constants as well as + the pool register setup instruction. */ + +static void +s390_mainpool_finish (struct constant_pool *pool) +{ + rtx base_reg = cfun->machine->base_reg; + rtx insn; + + /* If the pool is empty, we're done. */ + if (pool->size == 0) + { + /* We don't actually need a base register after all. */ + cfun->machine->base_reg = NULL_RTX; + + if (pool->pool_insn) + remove_insn (pool->pool_insn); + s390_free_pool (pool); + return; + } + + /* We need correct insn addresses. */ + shorten_branches (get_insns ()); + + /* On zSeries, we use a LARL to load the pool register. The pool is + located in the .rodata section, so we emit it after the function. */ + if (TARGET_CPU_ZARCH) + { + insn = gen_main_base_64 (base_reg, pool->label); + insn = emit_insn_after (insn, pool->pool_insn); + INSN_ADDRESSES_NEW (insn, -1); + remove_insn (pool->pool_insn); + + insn = get_last_insn (); + pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn); + INSN_ADDRESSES_NEW (pool->pool_insn, -1); + + s390_dump_pool (pool, 0); + } + + /* On S/390, if the total size of the function's code plus literal pool + does not exceed 4096 bytes, we use BASR to set up a function base + pointer, and emit the literal pool at the end of the function. */ + else if (INSN_ADDRESSES (INSN_UID (pool->emit_pool_after)) + + pool->size + 8 /* alignment slop */ < 4096) + { + insn = gen_main_base_31_small (base_reg, pool->label); + insn = emit_insn_after (insn, pool->pool_insn); + INSN_ADDRESSES_NEW (insn, -1); + remove_insn (pool->pool_insn); + + insn = emit_label_after (pool->label, insn); + INSN_ADDRESSES_NEW (insn, -1); + + /* emit_pool_after will be set by s390_mainpool_start to the + last insn of the section where the literal pool should be + emitted. */ + insn = pool->emit_pool_after; + + pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn); + INSN_ADDRESSES_NEW (pool->pool_insn, -1); + + s390_dump_pool (pool, 1); + } + + /* Otherwise, we emit an inline literal pool and use BASR to branch + over it, setting up the pool register at the same time. */ + else + { + rtx pool_end = gen_label_rtx (); + + insn = gen_main_base_31_large (base_reg, pool->label, pool_end); + insn = emit_insn_after (insn, pool->pool_insn); + INSN_ADDRESSES_NEW (insn, -1); + remove_insn (pool->pool_insn); + + insn = emit_label_after (pool->label, insn); + INSN_ADDRESSES_NEW (insn, -1); + + pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn); + INSN_ADDRESSES_NEW (pool->pool_insn, -1); + + insn = emit_label_after (pool_end, pool->pool_insn); + INSN_ADDRESSES_NEW (insn, -1); + + s390_dump_pool (pool, 1); + } + + + /* Replace all literal pool references. */ + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + if (INSN_P (insn)) + replace_ltrel_base (&PATTERN (insn)); + + if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) + { + rtx addr, pool_ref = NULL_RTX; + find_constant_pool_ref (PATTERN (insn), &pool_ref); + if (pool_ref) + { + if (s390_execute_label (insn)) + addr = s390_find_execute (pool, insn); + else + addr = s390_find_constant (pool, get_pool_constant (pool_ref), + get_pool_mode (pool_ref)); + + replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr); + INSN_CODE (insn) = -1; + } + } + } + + + /* Free the pool. */ + s390_free_pool (pool); +} + +/* POOL holds the main literal pool as collected by s390_mainpool_start. + We have decided we cannot use this pool, so revert all changes + to the current function that were done by s390_mainpool_start. */ +static void +s390_mainpool_cancel (struct constant_pool *pool) +{ + /* We didn't actually change the instruction stream, so simply + free the pool memory. */ + s390_free_pool (pool); +} + + +/* Chunkify the literal pool. */ + +#define S390_POOL_CHUNK_MIN 0xc00 +#define S390_POOL_CHUNK_MAX 0xe00 + +static struct constant_pool * +s390_chunkify_start (void) +{ + struct constant_pool *curr_pool = NULL, *pool_list = NULL; + int extra_size = 0; + bitmap far_labels; + rtx pending_ltrel = NULL_RTX; + rtx insn; + + rtx (*gen_reload_base) (rtx, rtx) = + TARGET_CPU_ZARCH? gen_reload_base_64 : gen_reload_base_31; + + + /* We need correct insn addresses. */ + + shorten_branches (get_insns ()); + + /* Scan all insns and move literals to pool chunks. */ + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + bool section_switch_p = false; + + /* Check for pending LTREL_BASE. */ + if (INSN_P (insn)) + { + rtx ltrel_base = find_ltrel_base (PATTERN (insn)); + if (ltrel_base) + { + gcc_assert (ltrel_base == pending_ltrel); + pending_ltrel = NULL_RTX; + } + } + + if (!TARGET_CPU_ZARCH && s390_execute_label (insn)) + { + if (!curr_pool) + curr_pool = s390_start_pool (&pool_list, insn); + + s390_add_execute (curr_pool, insn); + s390_add_pool_insn (curr_pool, insn); + } + else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) + { + rtx pool_ref = NULL_RTX; + find_constant_pool_ref (PATTERN (insn), &pool_ref); + if (pool_ref) + { + rtx constant = get_pool_constant (pool_ref); + enum machine_mode mode = get_pool_mode (pool_ref); + + if (!curr_pool) + curr_pool = s390_start_pool (&pool_list, insn); + + s390_add_constant (curr_pool, constant, mode); + s390_add_pool_insn (curr_pool, insn); + + /* Don't split the pool chunk between a LTREL_OFFSET load + and the corresponding LTREL_BASE. */ + if (GET_CODE (constant) == CONST + && GET_CODE (XEXP (constant, 0)) == UNSPEC + && XINT (XEXP (constant, 0), 1) == UNSPEC_LTREL_OFFSET) + { + gcc_assert (!pending_ltrel); + pending_ltrel = pool_ref; + } + } + } + + if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CODE_LABEL) + { + if (curr_pool) + s390_add_pool_insn (curr_pool, insn); + /* An LTREL_BASE must follow within the same basic block. */ + gcc_assert (!pending_ltrel); + } + + if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS) + section_switch_p = true; + + if (!curr_pool + || INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn) + || INSN_ADDRESSES (INSN_UID (insn)) == -1) + continue; + + if (TARGET_CPU_ZARCH) + { + if (curr_pool->size < S390_POOL_CHUNK_MAX) + continue; + + s390_end_pool (curr_pool, NULL_RTX); + curr_pool = NULL; + } + else + { + int chunk_size = INSN_ADDRESSES (INSN_UID (insn)) + - INSN_ADDRESSES (INSN_UID (curr_pool->first_insn)) + + extra_size; + + /* We will later have to insert base register reload insns. + Those will have an effect on code size, which we need to + consider here. This calculation makes rather pessimistic + worst-case assumptions. */ + if (GET_CODE (insn) == CODE_LABEL) + extra_size += 6; + + if (chunk_size < S390_POOL_CHUNK_MIN + && curr_pool->size < S390_POOL_CHUNK_MIN + && !section_switch_p) + continue; + + /* Pool chunks can only be inserted after BARRIERs ... */ + if (GET_CODE (insn) == BARRIER) + { + s390_end_pool (curr_pool, insn); + curr_pool = NULL; + extra_size = 0; + } + + /* ... so if we don't find one in time, create one. */ + else if (chunk_size > S390_POOL_CHUNK_MAX + || curr_pool->size > S390_POOL_CHUNK_MAX + || section_switch_p) + { + rtx label, jump, barrier; + + if (!section_switch_p) + { + /* We can insert the barrier only after a 'real' insn. */ + if (GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN) + continue; + if (get_attr_length (insn) == 0) + continue; + /* Don't separate LTREL_BASE from the corresponding + LTREL_OFFSET load. */ + if (pending_ltrel) + continue; + } + else + { + gcc_assert (!pending_ltrel); + + /* The old pool has to end before the section switch + note in order to make it part of the current + section. */ + insn = PREV_INSN (insn); + } + + label = gen_label_rtx (); + jump = emit_jump_insn_after (gen_jump (label), insn); + barrier = emit_barrier_after (jump); + insn = emit_label_after (label, barrier); + JUMP_LABEL (jump) = label; + LABEL_NUSES (label) = 1; + + INSN_ADDRESSES_NEW (jump, -1); + INSN_ADDRESSES_NEW (barrier, -1); + INSN_ADDRESSES_NEW (insn, -1); + + s390_end_pool (curr_pool, barrier); + curr_pool = NULL; + extra_size = 0; + } + } + } + + if (curr_pool) + s390_end_pool (curr_pool, NULL_RTX); + gcc_assert (!pending_ltrel); + + /* Find all labels that are branched into + from an insn belonging to a different chunk. */ + + far_labels = BITMAP_ALLOC (NULL); + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + /* Labels marked with LABEL_PRESERVE_P can be target + of non-local jumps, so we have to mark them. + The same holds for named labels. + + Don't do that, however, if it is the label before + a jump table. */ + + if (GET_CODE (insn) == CODE_LABEL + && (LABEL_PRESERVE_P (insn) || LABEL_NAME (insn))) + { + rtx vec_insn = next_real_insn (insn); + rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ? + PATTERN (vec_insn) : NULL_RTX; + if (!vec_pat + || !(GET_CODE (vec_pat) == ADDR_VEC + || GET_CODE (vec_pat) == ADDR_DIFF_VEC)) + bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (insn)); + } + + /* If we have a direct jump (conditional or unconditional) + or a casesi jump, check all potential targets. */ + else if (GET_CODE (insn) == JUMP_INSN) + { + rtx pat = PATTERN (insn); + if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2) + pat = XVECEXP (pat, 0, 0); + + if (GET_CODE (pat) == SET) + { + rtx label = JUMP_LABEL (insn); + if (label) + { + if (s390_find_pool (pool_list, label) + != s390_find_pool (pool_list, insn)) + bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label)); + } + } + else if (GET_CODE (pat) == PARALLEL + && XVECLEN (pat, 0) == 2 + && GET_CODE (XVECEXP (pat, 0, 0)) == SET + && GET_CODE (XVECEXP (pat, 0, 1)) == USE + && GET_CODE (XEXP (XVECEXP (pat, 0, 1), 0)) == LABEL_REF) + { + /* Find the jump table used by this casesi jump. */ + rtx vec_label = XEXP (XEXP (XVECEXP (pat, 0, 1), 0), 0); + rtx vec_insn = next_real_insn (vec_label); + rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ? + PATTERN (vec_insn) : NULL_RTX; + if (vec_pat + && (GET_CODE (vec_pat) == ADDR_VEC + || GET_CODE (vec_pat) == ADDR_DIFF_VEC)) + { + int i, diff_p = GET_CODE (vec_pat) == ADDR_DIFF_VEC; + + for (i = 0; i < XVECLEN (vec_pat, diff_p); i++) + { + rtx label = XEXP (XVECEXP (vec_pat, diff_p, i), 0); + + if (s390_find_pool (pool_list, label) + != s390_find_pool (pool_list, insn)) + bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label)); + } + } + } + } + } + + /* Insert base register reload insns before every pool. */ + + for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) + { + rtx new_insn = gen_reload_base (cfun->machine->base_reg, + curr_pool->label); + rtx insn = curr_pool->first_insn; + INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1); + } + + /* Insert base register reload insns at every far label. */ + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (GET_CODE (insn) == CODE_LABEL + && bitmap_bit_p (far_labels, CODE_LABEL_NUMBER (insn))) + { + struct constant_pool *pool = s390_find_pool (pool_list, insn); + if (pool) + { + rtx new_insn = gen_reload_base (cfun->machine->base_reg, + pool->label); + INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1); + } + } + + + BITMAP_FREE (far_labels); + + + /* Recompute insn addresses. */ + + init_insn_lengths (); + shorten_branches (get_insns ()); + + return pool_list; +} + +/* POOL_LIST is a chunk list as prepared by s390_chunkify_start. + After we have decided to use this list, finish implementing + all changes to the current function as required. */ + +static void +s390_chunkify_finish (struct constant_pool *pool_list) +{ + struct constant_pool *curr_pool = NULL; + rtx insn; + + + /* Replace all literal pool references. */ + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + if (INSN_P (insn)) + replace_ltrel_base (&PATTERN (insn)); + + curr_pool = s390_find_pool (pool_list, insn); + if (!curr_pool) + continue; + + if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN) + { + rtx addr, pool_ref = NULL_RTX; + find_constant_pool_ref (PATTERN (insn), &pool_ref); + if (pool_ref) + { + if (s390_execute_label (insn)) + addr = s390_find_execute (curr_pool, insn); + else + addr = s390_find_constant (curr_pool, + get_pool_constant (pool_ref), + get_pool_mode (pool_ref)); + + replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr); + INSN_CODE (insn) = -1; + } + } + } + + /* Dump out all literal pools. */ + + for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) + s390_dump_pool (curr_pool, 0); + + /* Free pool list. */ + + while (pool_list) + { + struct constant_pool *next = pool_list->next; + s390_free_pool (pool_list); + pool_list = next; + } +} + +/* POOL_LIST is a chunk list as prepared by s390_chunkify_start. + We have decided we cannot use this list, so revert all changes + to the current function that were done by s390_chunkify_start. */ + +static void +s390_chunkify_cancel (struct constant_pool *pool_list) +{ + struct constant_pool *curr_pool = NULL; + rtx insn; + + /* Remove all pool placeholder insns. */ + + for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next) + { + /* Did we insert an extra barrier? Remove it. */ + rtx barrier = PREV_INSN (curr_pool->pool_insn); + rtx jump = barrier? PREV_INSN (barrier) : NULL_RTX; + rtx label = NEXT_INSN (curr_pool->pool_insn); + + if (jump && GET_CODE (jump) == JUMP_INSN + && barrier && GET_CODE (barrier) == BARRIER + && label && GET_CODE (label) == CODE_LABEL + && GET_CODE (PATTERN (jump)) == SET + && SET_DEST (PATTERN (jump)) == pc_rtx + && GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF + && XEXP (SET_SRC (PATTERN (jump)), 0) == label) + { + remove_insn (jump); + remove_insn (barrier); + remove_insn (label); + } + + remove_insn (curr_pool->pool_insn); + } + + /* Remove all base register reload insns. */ + + for (insn = get_insns (); insn; ) + { + rtx next_insn = NEXT_INSN (insn); + + if (GET_CODE (insn) == INSN + && GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC + && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_RELOAD_BASE) + remove_insn (insn); + + insn = next_insn; + } + + /* Free pool list. */ + + while (pool_list) + { + struct constant_pool *next = pool_list->next; + s390_free_pool (pool_list); + pool_list = next; + } +} + +/* Output the constant pool entry EXP in mode MODE with alignment ALIGN. */ + +void +s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align) +{ + REAL_VALUE_TYPE r; + + switch (GET_MODE_CLASS (mode)) + { + case MODE_FLOAT: + case MODE_DECIMAL_FLOAT: + gcc_assert (GET_CODE (exp) == CONST_DOUBLE); + + REAL_VALUE_FROM_CONST_DOUBLE (r, exp); + assemble_real (r, mode, align); + break; + + case MODE_INT: + assemble_integer (exp, GET_MODE_SIZE (mode), align, 1); + mark_symbol_refs_as_used (exp); + break; + + default: + gcc_unreachable (); + } +} + + +/* Return an RTL expression representing the value of the return address + for the frame COUNT steps up from the current frame. FRAME is the + frame pointer of that frame. */ + +rtx +s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED) +{ + int offset; + rtx addr; + + /* Without backchain, we fail for all but the current frame. */ + + if (!TARGET_BACKCHAIN && count > 0) + return NULL_RTX; + + /* For the current frame, we need to make sure the initial + value of RETURN_REGNUM is actually saved. */ + + if (count == 0) + { + /* On non-z architectures branch splitting could overwrite r14. */ + if (TARGET_CPU_ZARCH) + return get_hard_reg_initial_val (Pmode, RETURN_REGNUM); + else + { + cfun_frame_layout.save_return_addr_p = true; + return gen_rtx_MEM (Pmode, return_address_pointer_rtx); + } + } + + if (TARGET_PACKED_STACK) + offset = -2 * UNITS_PER_LONG; + else + offset = RETURN_REGNUM * UNITS_PER_LONG; + + addr = plus_constant (frame, offset); + addr = memory_address (Pmode, addr); + return gen_rtx_MEM (Pmode, addr); +} + +/* Return an RTL expression representing the back chain stored in + the current stack frame. */ + +rtx +s390_back_chain_rtx (void) +{ + rtx chain; + + gcc_assert (TARGET_BACKCHAIN); + + if (TARGET_PACKED_STACK) + chain = plus_constant (stack_pointer_rtx, + STACK_POINTER_OFFSET - UNITS_PER_LONG); + else + chain = stack_pointer_rtx; + + chain = gen_rtx_MEM (Pmode, chain); + return chain; +} + +/* Find first call clobbered register unused in a function. + This could be used as base register in a leaf function + or for holding the return address before epilogue. */ + +static int +find_unused_clobbered_reg (void) +{ + int i; + for (i = 0; i < 6; i++) + if (!df_regs_ever_live_p (i)) + return i; + return 0; +} + + +/* Helper function for s390_regs_ever_clobbered. Sets the fields in DATA for all + clobbered hard regs in SETREG. */ + +static void +s390_reg_clobbered_rtx (rtx setreg, const_rtx set_insn ATTRIBUTE_UNUSED, void *data) +{ + int *regs_ever_clobbered = (int *)data; + unsigned int i, regno; + enum machine_mode mode = GET_MODE (setreg); + + if (GET_CODE (setreg) == SUBREG) + { + rtx inner = SUBREG_REG (setreg); + if (!GENERAL_REG_P (inner)) + return; + regno = subreg_regno (setreg); + } + else if (GENERAL_REG_P (setreg)) + regno = REGNO (setreg); + else + return; + + for (i = regno; + i < regno + HARD_REGNO_NREGS (regno, mode); + i++) + regs_ever_clobbered[i] = 1; +} + +/* Walks through all basic blocks of the current function looking + for clobbered hard regs using s390_reg_clobbered_rtx. The fields + of the passed integer array REGS_EVER_CLOBBERED are set to one for + each of those regs. */ + +static void +s390_regs_ever_clobbered (int *regs_ever_clobbered) +{ + basic_block cur_bb; + rtx cur_insn; + unsigned int i; + + memset (regs_ever_clobbered, 0, 16 * sizeof (int)); + + /* For non-leaf functions we have to consider all call clobbered regs to be + clobbered. */ + if (!current_function_is_leaf) + { + for (i = 0; i < 16; i++) + regs_ever_clobbered[i] = call_really_used_regs[i]; + } + + /* Make the "magic" eh_return registers live if necessary. For regs_ever_live + this work is done by liveness analysis (mark_regs_live_at_end). + Special care is needed for functions containing landing pads. Landing pads + may use the eh registers, but the code which sets these registers is not + contained in that function. Hence s390_regs_ever_clobbered is not able to + deal with this automatically. */ + if (crtl->calls_eh_return || cfun->machine->has_landing_pad_p) + for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM ; i++) + if (crtl->calls_eh_return + || (cfun->machine->has_landing_pad_p + && df_regs_ever_live_p (EH_RETURN_DATA_REGNO (i)))) + regs_ever_clobbered[EH_RETURN_DATA_REGNO (i)] = 1; + + /* For nonlocal gotos all call-saved registers have to be saved. + This flag is also set for the unwinding code in libgcc. + See expand_builtin_unwind_init. For regs_ever_live this is done by + reload. */ + if (cfun->has_nonlocal_label) + for (i = 0; i < 16; i++) + if (!call_really_used_regs[i]) + regs_ever_clobbered[i] = 1; + + FOR_EACH_BB (cur_bb) + { + FOR_BB_INSNS (cur_bb, cur_insn) + { + if (INSN_P (cur_insn)) + note_stores (PATTERN (cur_insn), + s390_reg_clobbered_rtx, + regs_ever_clobbered); + } + } +} + +/* Determine the frame area which actually has to be accessed + in the function epilogue. The values are stored at the + given pointers AREA_BOTTOM (address of the lowest used stack + address) and AREA_TOP (address of the first item which does + not belong to the stack frame). */ + +static void +s390_frame_area (int *area_bottom, int *area_top) +{ + int b, t; + int i; + + b = INT_MAX; + t = INT_MIN; + + if (cfun_frame_layout.first_restore_gpr != -1) + { + b = (cfun_frame_layout.gprs_offset + + cfun_frame_layout.first_restore_gpr * UNITS_PER_LONG); + t = b + (cfun_frame_layout.last_restore_gpr + - cfun_frame_layout.first_restore_gpr + 1) * UNITS_PER_LONG; + } + + if (TARGET_64BIT && cfun_save_high_fprs_p) + { + b = MIN (b, cfun_frame_layout.f8_offset); + t = MAX (t, (cfun_frame_layout.f8_offset + + cfun_frame_layout.high_fprs * 8)); + } + + if (!TARGET_64BIT) + for (i = 2; i < 4; i++) + if (cfun_fpr_bit_p (i)) + { + b = MIN (b, cfun_frame_layout.f4_offset + (i - 2) * 8); + t = MAX (t, cfun_frame_layout.f4_offset + (i - 1) * 8); + } + + *area_bottom = b; + *area_top = t; +} + +/* Fill cfun->machine with info about register usage of current function. + Return in CLOBBERED_REGS which GPRs are currently considered set. */ + +static void +s390_register_info (int clobbered_regs[]) +{ + int i, j; + + /* fprs 8 - 15 are call saved for 64 Bit ABI. */ + cfun_frame_layout.fpr_bitmap = 0; + cfun_frame_layout.high_fprs = 0; + if (TARGET_64BIT) + for (i = 24; i < 32; i++) + if (df_regs_ever_live_p (i) && !global_regs[i]) + { + cfun_set_fpr_bit (i - 16); + cfun_frame_layout.high_fprs++; + } + + /* Find first and last gpr to be saved. We trust regs_ever_live + data, except that we don't save and restore global registers. + + Also, all registers with special meaning to the compiler need + to be handled extra. */ + + s390_regs_ever_clobbered (clobbered_regs); + + for (i = 0; i < 16; i++) + clobbered_regs[i] = clobbered_regs[i] && !global_regs[i] && !fixed_regs[i]; + + if (frame_pointer_needed) + clobbered_regs[HARD_FRAME_POINTER_REGNUM] = 1; + + if (flag_pic) + clobbered_regs[PIC_OFFSET_TABLE_REGNUM] + |= df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM); + + clobbered_regs[BASE_REGNUM] + |= (cfun->machine->base_reg + && REGNO (cfun->machine->base_reg) == BASE_REGNUM); + + clobbered_regs[RETURN_REGNUM] + |= (!current_function_is_leaf + || TARGET_TPF_PROFILING + || cfun->machine->split_branches_pending_p + || cfun_frame_layout.save_return_addr_p + || crtl->calls_eh_return + || cfun->stdarg); + + clobbered_regs[STACK_POINTER_REGNUM] + |= (!current_function_is_leaf + || TARGET_TPF_PROFILING + || cfun_save_high_fprs_p + || get_frame_size () > 0 + || cfun->calls_alloca + || cfun->stdarg); + + for (i = 6; i < 16; i++) + if (df_regs_ever_live_p (i) || clobbered_regs[i]) + break; + for (j = 15; j > i; j--) + if (df_regs_ever_live_p (j) || clobbered_regs[j]) + break; + + if (i == 16) + { + /* Nothing to save/restore. */ + cfun_frame_layout.first_save_gpr_slot = -1; + cfun_frame_layout.last_save_gpr_slot = -1; + cfun_frame_layout.first_save_gpr = -1; + cfun_frame_layout.first_restore_gpr = -1; + cfun_frame_layout.last_save_gpr = -1; + cfun_frame_layout.last_restore_gpr = -1; + } + else + { + /* Save slots for gprs from i to j. */ + cfun_frame_layout.first_save_gpr_slot = i; + cfun_frame_layout.last_save_gpr_slot = j; + + for (i = cfun_frame_layout.first_save_gpr_slot; + i < cfun_frame_layout.last_save_gpr_slot + 1; + i++) + if (clobbered_regs[i]) + break; + + for (j = cfun_frame_layout.last_save_gpr_slot; j > i; j--) + if (clobbered_regs[j]) + break; + + if (i == cfun_frame_layout.last_save_gpr_slot + 1) + { + /* Nothing to save/restore. */ + cfun_frame_layout.first_save_gpr = -1; + cfun_frame_layout.first_restore_gpr = -1; + cfun_frame_layout.last_save_gpr = -1; + cfun_frame_layout.last_restore_gpr = -1; + } + else + { + /* Save / Restore from gpr i to j. */ + cfun_frame_layout.first_save_gpr = i; + cfun_frame_layout.first_restore_gpr = i; + cfun_frame_layout.last_save_gpr = j; + cfun_frame_layout.last_restore_gpr = j; + } + } + + if (cfun->stdarg) + { + /* Varargs functions need to save gprs 2 to 6. */ + if (cfun->va_list_gpr_size + && crtl->args.info.gprs < GP_ARG_NUM_REG) + { + int min_gpr = crtl->args.info.gprs; + int max_gpr = min_gpr + cfun->va_list_gpr_size; + if (max_gpr > GP_ARG_NUM_REG) + max_gpr = GP_ARG_NUM_REG; + + if (cfun_frame_layout.first_save_gpr == -1 + || cfun_frame_layout.first_save_gpr > 2 + min_gpr) + { + cfun_frame_layout.first_save_gpr = 2 + min_gpr; + cfun_frame_layout.first_save_gpr_slot = 2 + min_gpr; + } + + if (cfun_frame_layout.last_save_gpr == -1 + || cfun_frame_layout.last_save_gpr < 2 + max_gpr - 1) + { + cfun_frame_layout.last_save_gpr = 2 + max_gpr - 1; + cfun_frame_layout.last_save_gpr_slot = 2 + max_gpr - 1; + } + } + + /* Mark f0, f2 for 31 bit and f0-f4 for 64 bit to be saved. */ + if (TARGET_HARD_FLOAT && cfun->va_list_fpr_size + && crtl->args.info.fprs < FP_ARG_NUM_REG) + { + int min_fpr = crtl->args.info.fprs; + int max_fpr = min_fpr + cfun->va_list_fpr_size; + if (max_fpr > FP_ARG_NUM_REG) + max_fpr = FP_ARG_NUM_REG; + + /* ??? This is currently required to ensure proper location + of the fpr save slots within the va_list save area. */ + if (TARGET_PACKED_STACK) + min_fpr = 0; + + for (i = min_fpr; i < max_fpr; i++) + cfun_set_fpr_bit (i); + } + } + + if (!TARGET_64BIT) + for (i = 2; i < 4; i++) + if (df_regs_ever_live_p (i + 16) && !global_regs[i + 16]) + cfun_set_fpr_bit (i); +} + +/* Fill cfun->machine with info about frame of current function. */ + +static void +s390_frame_info (void) +{ + int i; + + cfun_frame_layout.frame_size = get_frame_size (); + if (!TARGET_64BIT && cfun_frame_layout.frame_size > 0x7fff0000) + fatal_error ("total size of local variables exceeds architecture limit"); + + if (!TARGET_PACKED_STACK) + { + cfun_frame_layout.backchain_offset = 0; + cfun_frame_layout.f0_offset = 16 * UNITS_PER_LONG; + cfun_frame_layout.f4_offset = cfun_frame_layout.f0_offset + 2 * 8; + cfun_frame_layout.f8_offset = -cfun_frame_layout.high_fprs * 8; + cfun_frame_layout.gprs_offset = (cfun_frame_layout.first_save_gpr_slot + * UNITS_PER_LONG); + } + else if (TARGET_BACKCHAIN) /* kernel stack layout */ + { + cfun_frame_layout.backchain_offset = (STACK_POINTER_OFFSET + - UNITS_PER_LONG); + cfun_frame_layout.gprs_offset + = (cfun_frame_layout.backchain_offset + - (STACK_POINTER_REGNUM - cfun_frame_layout.first_save_gpr_slot + 1) + * UNITS_PER_LONG); + + if (TARGET_64BIT) + { + cfun_frame_layout.f4_offset + = (cfun_frame_layout.gprs_offset + - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3))); + + cfun_frame_layout.f0_offset + = (cfun_frame_layout.f4_offset + - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1))); + } + else + { + /* On 31 bit we have to care about alignment of the + floating point regs to provide fastest access. */ + cfun_frame_layout.f0_offset + = ((cfun_frame_layout.gprs_offset + & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1)) + - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1))); + + cfun_frame_layout.f4_offset + = (cfun_frame_layout.f0_offset + - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3))); + } + } + else /* no backchain */ + { + cfun_frame_layout.f4_offset + = (STACK_POINTER_OFFSET + - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3))); + + cfun_frame_layout.f0_offset + = (cfun_frame_layout.f4_offset + - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1))); + + cfun_frame_layout.gprs_offset + = cfun_frame_layout.f0_offset - cfun_gprs_save_area_size; + } + + if (current_function_is_leaf + && !TARGET_TPF_PROFILING + && cfun_frame_layout.frame_size == 0 + && !cfun_save_high_fprs_p + && !cfun->calls_alloca + && !cfun->stdarg) + return; + + if (!TARGET_PACKED_STACK) + cfun_frame_layout.frame_size += (STACK_POINTER_OFFSET + + crtl->outgoing_args_size + + cfun_frame_layout.high_fprs * 8); + else + { + if (TARGET_BACKCHAIN) + cfun_frame_layout.frame_size += UNITS_PER_LONG; + + /* No alignment trouble here because f8-f15 are only saved under + 64 bit. */ + cfun_frame_layout.f8_offset = (MIN (MIN (cfun_frame_layout.f0_offset, + cfun_frame_layout.f4_offset), + cfun_frame_layout.gprs_offset) + - cfun_frame_layout.high_fprs * 8); + + cfun_frame_layout.frame_size += cfun_frame_layout.high_fprs * 8; + + for (i = 0; i < 8; i++) + if (cfun_fpr_bit_p (i)) + cfun_frame_layout.frame_size += 8; + + cfun_frame_layout.frame_size += cfun_gprs_save_area_size; + + /* If under 31 bit an odd number of gprs has to be saved we have to adjust + the frame size to sustain 8 byte alignment of stack frames. */ + cfun_frame_layout.frame_size = ((cfun_frame_layout.frame_size + + STACK_BOUNDARY / BITS_PER_UNIT - 1) + & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1)); + + cfun_frame_layout.frame_size += crtl->outgoing_args_size; + } +} + +/* Generate frame layout. Fills in register and frame data for the current + function in cfun->machine. This routine can be called multiple times; + it will re-do the complete frame layout every time. */ + +static void +s390_init_frame_layout (void) +{ + HOST_WIDE_INT frame_size; + int base_used; + int clobbered_regs[16]; + + /* On S/390 machines, we may need to perform branch splitting, which + will require both base and return address register. We have no + choice but to assume we're going to need them until right at the + end of the machine dependent reorg phase. */ + if (!TARGET_CPU_ZARCH) + cfun->machine->split_branches_pending_p = true; + + do + { + frame_size = cfun_frame_layout.frame_size; + + /* Try to predict whether we'll need the base register. */ + base_used = cfun->machine->split_branches_pending_p + || crtl->uses_const_pool + || (!DISP_IN_RANGE (frame_size) + && !CONST_OK_FOR_K (frame_size)); + + /* Decide which register to use as literal pool base. In small + leaf functions, try to use an unused call-clobbered register + as base register to avoid save/restore overhead. */ + if (!base_used) + cfun->machine->base_reg = NULL_RTX; + else if (current_function_is_leaf && !df_regs_ever_live_p (5)) + cfun->machine->base_reg = gen_rtx_REG (Pmode, 5); + else + cfun->machine->base_reg = gen_rtx_REG (Pmode, BASE_REGNUM); + + s390_register_info (clobbered_regs); + s390_frame_info (); + } + while (frame_size != cfun_frame_layout.frame_size); +} + +/* Update frame layout. Recompute actual register save data based on + current info and update regs_ever_live for the special registers. + May be called multiple times, but may never cause *more* registers + to be saved than s390_init_frame_layout allocated room for. */ + +static void +s390_update_frame_layout (void) +{ + int clobbered_regs[16]; + + s390_register_info (clobbered_regs); + + df_set_regs_ever_live (BASE_REGNUM, + clobbered_regs[BASE_REGNUM] ? true : false); + df_set_regs_ever_live (RETURN_REGNUM, + clobbered_regs[RETURN_REGNUM] ? true : false); + df_set_regs_ever_live (STACK_POINTER_REGNUM, + clobbered_regs[STACK_POINTER_REGNUM] ? true : false); + + if (cfun->machine->base_reg) + df_set_regs_ever_live (REGNO (cfun->machine->base_reg), true); +} + +/* Return true if it is legal to put a value with MODE into REGNO. */ + +bool +s390_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode) +{ + switch (REGNO_REG_CLASS (regno)) + { + case FP_REGS: + if (REGNO_PAIR_OK (regno, mode)) + { + if (mode == SImode || mode == DImode) + return true; + + if (FLOAT_MODE_P (mode) && GET_MODE_CLASS (mode) != MODE_VECTOR_FLOAT) + return true; + } + break; + case ADDR_REGS: + if (FRAME_REGNO_P (regno) && mode == Pmode) + return true; + + /* fallthrough */ + case GENERAL_REGS: + if (REGNO_PAIR_OK (regno, mode)) + { + if (TARGET_ZARCH + || (mode != TFmode && mode != TCmode && mode != TDmode)) + return true; + } + break; + case CC_REGS: + if (GET_MODE_CLASS (mode) == MODE_CC) + return true; + break; + case ACCESS_REGS: + if (REGNO_PAIR_OK (regno, mode)) + { + if (mode == SImode || mode == Pmode) + return true; + } + break; + default: + return false; + } + + return false; +} + +/* Return nonzero if register OLD_REG can be renamed to register NEW_REG. */ + +bool +s390_hard_regno_rename_ok (unsigned int old_reg, unsigned int new_reg) +{ + /* Once we've decided upon a register to use as base register, it must + no longer be used for any other purpose. */ + if (cfun->machine->base_reg) + if (REGNO (cfun->machine->base_reg) == old_reg + || REGNO (cfun->machine->base_reg) == new_reg) + return false; + + return true; +} + +/* Maximum number of registers to represent a value of mode MODE + in a register of class RCLASS. */ + +int +s390_class_max_nregs (enum reg_class rclass, enum machine_mode mode) +{ + switch (rclass) + { + case FP_REGS: + if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT) + return 2 * ((GET_MODE_SIZE (mode) / 2 + 8 - 1) / 8); + else + return (GET_MODE_SIZE (mode) + 8 - 1) / 8; + case ACCESS_REGS: + return (GET_MODE_SIZE (mode) + 4 - 1) / 4; + default: + break; + } + return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; +} + +/* Return true if register FROM can be eliminated via register TO. */ + +static bool +s390_can_eliminate (const int from, const int to) +{ + /* On zSeries machines, we have not marked the base register as fixed. + Instead, we have an elimination rule BASE_REGNUM -> BASE_REGNUM. + If a function requires the base register, we say here that this + elimination cannot be performed. This will cause reload to free + up the base register (as if it were fixed). On the other hand, + if the current function does *not* require the base register, we + say here the elimination succeeds, which in turn allows reload + to allocate the base register for any other purpose. */ + if (from == BASE_REGNUM && to == BASE_REGNUM) + { + if (TARGET_CPU_ZARCH) + { + s390_init_frame_layout (); + return cfun->machine->base_reg == NULL_RTX; + } + + return false; + } + + /* Everything else must point into the stack frame. */ + gcc_assert (to == STACK_POINTER_REGNUM + || to == HARD_FRAME_POINTER_REGNUM); + + gcc_assert (from == FRAME_POINTER_REGNUM + || from == ARG_POINTER_REGNUM + || from == RETURN_ADDRESS_POINTER_REGNUM); + + /* Make sure we actually saved the return address. */ + if (from == RETURN_ADDRESS_POINTER_REGNUM) + if (!crtl->calls_eh_return + && !cfun->stdarg + && !cfun_frame_layout.save_return_addr_p) + return false; + + return true; +} + +/* Return offset between register FROM and TO initially after prolog. */ + +HOST_WIDE_INT +s390_initial_elimination_offset (int from, int to) +{ + HOST_WIDE_INT offset; + int index; + + /* ??? Why are we called for non-eliminable pairs? */ + if (!s390_can_eliminate (from, to)) + return 0; + + switch (from) + { + case FRAME_POINTER_REGNUM: + offset = (get_frame_size() + + STACK_POINTER_OFFSET + + crtl->outgoing_args_size); + break; + + case ARG_POINTER_REGNUM: + s390_init_frame_layout (); + offset = cfun_frame_layout.frame_size + STACK_POINTER_OFFSET; + break; + + case RETURN_ADDRESS_POINTER_REGNUM: + s390_init_frame_layout (); + index = RETURN_REGNUM - cfun_frame_layout.first_save_gpr_slot; + gcc_assert (index >= 0); + offset = cfun_frame_layout.frame_size + cfun_frame_layout.gprs_offset; + offset += index * UNITS_PER_LONG; + break; + + case BASE_REGNUM: + offset = 0; + break; + + default: + gcc_unreachable (); + } + + return offset; +} + +/* Emit insn to save fpr REGNUM at offset OFFSET relative + to register BASE. Return generated insn. */ + +static rtx +save_fpr (rtx base, int offset, int regnum) +{ + rtx addr; + addr = gen_rtx_MEM (DFmode, plus_constant (base, offset)); + + if (regnum >= 16 && regnum <= (16 + FP_ARG_NUM_REG)) + set_mem_alias_set (addr, get_varargs_alias_set ()); + else + set_mem_alias_set (addr, get_frame_alias_set ()); + + return emit_move_insn (addr, gen_rtx_REG (DFmode, regnum)); +} + +/* Emit insn to restore fpr REGNUM from offset OFFSET relative + to register BASE. Return generated insn. */ + +static rtx +restore_fpr (rtx base, int offset, int regnum) +{ + rtx addr; + addr = gen_rtx_MEM (DFmode, plus_constant (base, offset)); + set_mem_alias_set (addr, get_frame_alias_set ()); + + return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr); +} + +/* Return true if REGNO is a global register, but not one + of the special ones that need to be saved/restored in anyway. */ + +static inline bool +global_not_special_regno_p (int regno) +{ + return (global_regs[regno] + /* These registers are special and need to be + restored in any case. */ + && !(regno == STACK_POINTER_REGNUM + || regno == RETURN_REGNUM + || regno == BASE_REGNUM + || (flag_pic && regno == (int)PIC_OFFSET_TABLE_REGNUM))); +} + +/* Generate insn to save registers FIRST to LAST into + the register save area located at offset OFFSET + relative to register BASE. */ + +static rtx +save_gprs (rtx base, int offset, int first, int last) +{ + rtx addr, insn, note; + int i; + + addr = plus_constant (base, offset); + addr = gen_rtx_MEM (Pmode, addr); + + set_mem_alias_set (addr, get_frame_alias_set ()); + + /* Special-case single register. */ + if (first == last) + { + if (TARGET_64BIT) + insn = gen_movdi (addr, gen_rtx_REG (Pmode, first)); + else + insn = gen_movsi (addr, gen_rtx_REG (Pmode, first)); + + if (!global_not_special_regno_p (first)) + RTX_FRAME_RELATED_P (insn) = 1; + return insn; + } + + + insn = gen_store_multiple (addr, + gen_rtx_REG (Pmode, first), + GEN_INT (last - first + 1)); + + if (first <= 6 && cfun->stdarg) + for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) + { + rtx mem = XEXP (XVECEXP (PATTERN (insn), 0, i), 0); + + if (first + i <= 6) + set_mem_alias_set (mem, get_varargs_alias_set ()); + } + + /* We need to set the FRAME_RELATED flag on all SETs + inside the store-multiple pattern. + + However, we must not emit DWARF records for registers 2..5 + if they are stored for use by variable arguments ... + + ??? Unfortunately, it is not enough to simply not the + FRAME_RELATED flags for those SETs, because the first SET + of the PARALLEL is always treated as if it had the flag + set, even if it does not. Therefore we emit a new pattern + without those registers as REG_FRAME_RELATED_EXPR note. */ + + if (first >= 6 && !global_not_special_regno_p (first)) + { + rtx pat = PATTERN (insn); + + for (i = 0; i < XVECLEN (pat, 0); i++) + if (GET_CODE (XVECEXP (pat, 0, i)) == SET + && !global_not_special_regno_p (REGNO (SET_SRC (XVECEXP (pat, + 0, i))))) + RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1; + + RTX_FRAME_RELATED_P (insn) = 1; + } + else if (last >= 6) + { + int start; + + for (start = first >= 6 ? first : 6; start <= last; start++) + if (!global_not_special_regno_p (start)) + break; + + if (start > last) + return insn; + + addr = plus_constant (base, offset + (start - first) * UNITS_PER_LONG); + note = gen_store_multiple (gen_rtx_MEM (Pmode, addr), + gen_rtx_REG (Pmode, start), + GEN_INT (last - start + 1)); + note = PATTERN (note); + + add_reg_note (insn, REG_FRAME_RELATED_EXPR, note); + + for (i = 0; i < XVECLEN (note, 0); i++) + if (GET_CODE (XVECEXP (note, 0, i)) == SET + && !global_not_special_regno_p (REGNO (SET_SRC (XVECEXP (note, + 0, i))))) + RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1; + + RTX_FRAME_RELATED_P (insn) = 1; + } + + return insn; +} + +/* Generate insn to restore registers FIRST to LAST from + the register save area located at offset OFFSET + relative to register BASE. */ + +static rtx +restore_gprs (rtx base, int offset, int first, int last) +{ + rtx addr, insn; + + addr = plus_constant (base, offset); + addr = gen_rtx_MEM (Pmode, addr); + set_mem_alias_set (addr, get_frame_alias_set ()); + + /* Special-case single register. */ + if (first == last) + { + if (TARGET_64BIT) + insn = gen_movdi (gen_rtx_REG (Pmode, first), addr); + else + insn = gen_movsi (gen_rtx_REG (Pmode, first), addr); + + return insn; + } + + insn = gen_load_multiple (gen_rtx_REG (Pmode, first), + addr, + GEN_INT (last - first + 1)); + return insn; +} + +/* Return insn sequence to load the GOT register. */ + +static GTY(()) rtx got_symbol; +rtx +s390_load_got (void) +{ + rtx insns; + + if (!got_symbol) + { + got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); + SYMBOL_REF_FLAGS (got_symbol) = SYMBOL_FLAG_LOCAL; + } + + start_sequence (); + + if (TARGET_CPU_ZARCH) + { + emit_move_insn (pic_offset_table_rtx, got_symbol); + } + else + { + rtx offset; + + offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, got_symbol), + UNSPEC_LTREL_OFFSET); + offset = gen_rtx_CONST (Pmode, offset); + offset = force_const_mem (Pmode, offset); + + emit_move_insn (pic_offset_table_rtx, offset); + + offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, XEXP (offset, 0)), + UNSPEC_LTREL_BASE); + offset = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, offset); + + emit_move_insn (pic_offset_table_rtx, offset); + } + + insns = get_insns (); + end_sequence (); + return insns; +} + +/* This ties together stack memory (MEM with an alias set of frame_alias_set) + and the change to the stack pointer. */ + +static void +s390_emit_stack_tie (void) +{ + rtx mem = gen_frame_mem (BLKmode, + gen_rtx_REG (Pmode, STACK_POINTER_REGNUM)); + + emit_insn (gen_stack_tie (mem)); +} + +/* Expand the prologue into a bunch of separate insns. */ + +void +s390_emit_prologue (void) +{ + rtx insn, addr; + rtx temp_reg; + int i; + int offset; + int next_fpr = 0; + + /* Complete frame layout. */ + + s390_update_frame_layout (); + + /* Annotate all constant pool references to let the scheduler know + they implicitly use the base register. */ + + push_topmost_sequence (); + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (INSN_P (insn)) + { + annotate_constant_pool_refs (&PATTERN (insn)); + df_insn_rescan (insn); + } + + pop_topmost_sequence (); + + /* Choose best register to use for temp use within prologue. + See below for why TPF must use the register 1. */ + + if (!has_hard_reg_initial_val (Pmode, RETURN_REGNUM) + && !current_function_is_leaf + && !TARGET_TPF_PROFILING) + temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); + else + temp_reg = gen_rtx_REG (Pmode, 1); + + /* Save call saved gprs. */ + if (cfun_frame_layout.first_save_gpr != -1) + { + insn = save_gprs (stack_pointer_rtx, + cfun_frame_layout.gprs_offset + + UNITS_PER_LONG * (cfun_frame_layout.first_save_gpr + - cfun_frame_layout.first_save_gpr_slot), + cfun_frame_layout.first_save_gpr, + cfun_frame_layout.last_save_gpr); + emit_insn (insn); + } + + /* Dummy insn to mark literal pool slot. */ + + if (cfun->machine->base_reg) + emit_insn (gen_main_pool (cfun->machine->base_reg)); + + offset = cfun_frame_layout.f0_offset; + + /* Save f0 and f2. */ + for (i = 0; i < 2; i++) + { + if (cfun_fpr_bit_p (i)) + { + save_fpr (stack_pointer_rtx, offset, i + 16); + offset += 8; + } + else if (!TARGET_PACKED_STACK) + offset += 8; + } + + /* Save f4 and f6. */ + offset = cfun_frame_layout.f4_offset; + for (i = 2; i < 4; i++) + { + if (cfun_fpr_bit_p (i)) + { + insn = save_fpr (stack_pointer_rtx, offset, i + 16); + offset += 8; + + /* If f4 and f6 are call clobbered they are saved due to stdargs and + therefore are not frame related. */ + if (!call_really_used_regs[i + 16]) + RTX_FRAME_RELATED_P (insn) = 1; + } + else if (!TARGET_PACKED_STACK) + offset += 8; + } + + if (TARGET_PACKED_STACK + && cfun_save_high_fprs_p + && cfun_frame_layout.f8_offset + cfun_frame_layout.high_fprs * 8 > 0) + { + offset = (cfun_frame_layout.f8_offset + + (cfun_frame_layout.high_fprs - 1) * 8); + + for (i = 15; i > 7 && offset >= 0; i--) + if (cfun_fpr_bit_p (i)) + { + insn = save_fpr (stack_pointer_rtx, offset, i + 16); + + RTX_FRAME_RELATED_P (insn) = 1; + offset -= 8; + } + if (offset >= cfun_frame_layout.f8_offset) + next_fpr = i + 16; + } + + if (!TARGET_PACKED_STACK) + next_fpr = cfun_save_high_fprs_p ? 31 : 0; + + if (flag_stack_usage) + current_function_static_stack_size = cfun_frame_layout.frame_size; + + /* Decrement stack pointer. */ + + if (cfun_frame_layout.frame_size > 0) + { + rtx frame_off = GEN_INT (-cfun_frame_layout.frame_size); + rtx real_frame_off; + + if (s390_stack_size) + { + HOST_WIDE_INT stack_guard; + + if (s390_stack_guard) + stack_guard = s390_stack_guard; + else + { + /* If no value for stack guard is provided the smallest power of 2 + larger than the current frame size is chosen. */ + stack_guard = 1; + while (stack_guard < cfun_frame_layout.frame_size) + stack_guard <<= 1; + } + + if (cfun_frame_layout.frame_size >= s390_stack_size) + { + warning (0, "frame size of function %qs is " + HOST_WIDE_INT_PRINT_DEC + " bytes exceeding user provided stack limit of " + HOST_WIDE_INT_PRINT_DEC " bytes. " + "An unconditional trap is added.", + current_function_name(), cfun_frame_layout.frame_size, + s390_stack_size); + emit_insn (gen_trap ()); + } + else + { + /* stack_guard has to be smaller than s390_stack_size. + Otherwise we would emit an AND with zero which would + not match the test under mask pattern. */ + if (stack_guard >= s390_stack_size) + { + warning (0, "frame size of function %qs is " + HOST_WIDE_INT_PRINT_DEC + " bytes which is more than half the stack size. " + "The dynamic check would not be reliable. " + "No check emitted for this function.", + current_function_name(), + cfun_frame_layout.frame_size); + } + else + { + HOST_WIDE_INT stack_check_mask = ((s390_stack_size - 1) + & ~(stack_guard - 1)); + + rtx t = gen_rtx_AND (Pmode, stack_pointer_rtx, + GEN_INT (stack_check_mask)); + if (TARGET_64BIT) + emit_insn (gen_ctrapdi4 (gen_rtx_EQ (VOIDmode, + t, const0_rtx), + t, const0_rtx, const0_rtx)); + else + emit_insn (gen_ctrapsi4 (gen_rtx_EQ (VOIDmode, + t, const0_rtx), + t, const0_rtx, const0_rtx)); + } + } + } + + if (s390_warn_framesize > 0 + && cfun_frame_layout.frame_size >= s390_warn_framesize) + warning (0, "frame size of %qs is " HOST_WIDE_INT_PRINT_DEC " bytes", + current_function_name (), cfun_frame_layout.frame_size); + + if (s390_warn_dynamicstack_p && cfun->calls_alloca) + warning (0, "%qs uses dynamic stack allocation", current_function_name ()); + + /* Save incoming stack pointer into temp reg. */ + if (TARGET_BACKCHAIN || next_fpr) + insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx)); + + /* Subtract frame size from stack pointer. */ + + if (DISP_IN_RANGE (INTVAL (frame_off))) + { + insn = gen_rtx_SET (VOIDmode, stack_pointer_rtx, + gen_rtx_PLUS (Pmode, stack_pointer_rtx, + frame_off)); + insn = emit_insn (insn); + } + else + { + if (!CONST_OK_FOR_K (INTVAL (frame_off))) + frame_off = force_const_mem (Pmode, frame_off); + + insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off)); + annotate_constant_pool_refs (&PATTERN (insn)); + } + + RTX_FRAME_RELATED_P (insn) = 1; + real_frame_off = GEN_INT (-cfun_frame_layout.frame_size); + add_reg_note (insn, REG_FRAME_RELATED_EXPR, + gen_rtx_SET (VOIDmode, stack_pointer_rtx, + gen_rtx_PLUS (Pmode, stack_pointer_rtx, + real_frame_off))); + + /* Set backchain. */ + + if (TARGET_BACKCHAIN) + { + if (cfun_frame_layout.backchain_offset) + addr = gen_rtx_MEM (Pmode, + plus_constant (stack_pointer_rtx, + cfun_frame_layout.backchain_offset)); + else + addr = gen_rtx_MEM (Pmode, stack_pointer_rtx); + set_mem_alias_set (addr, get_frame_alias_set ()); + insn = emit_insn (gen_move_insn (addr, temp_reg)); + } + + /* If we support non-call exceptions (e.g. for Java), + we need to make sure the backchain pointer is set up + before any possibly trapping memory access. */ + if (TARGET_BACKCHAIN && cfun->can_throw_non_call_exceptions) + { + addr = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode)); + emit_clobber (addr); + } + } + + /* Save fprs 8 - 15 (64 bit ABI). */ + + if (cfun_save_high_fprs_p && next_fpr) + { + /* If the stack might be accessed through a different register + we have to make sure that the stack pointer decrement is not + moved below the use of the stack slots. */ + s390_emit_stack_tie (); + + insn = emit_insn (gen_add2_insn (temp_reg, + GEN_INT (cfun_frame_layout.f8_offset))); + + offset = 0; + + for (i = 24; i <= next_fpr; i++) + if (cfun_fpr_bit_p (i - 16)) + { + rtx addr = plus_constant (stack_pointer_rtx, + cfun_frame_layout.frame_size + + cfun_frame_layout.f8_offset + + offset); + + insn = save_fpr (temp_reg, offset, i); + offset += 8; + RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_FRAME_RELATED_EXPR, + gen_rtx_SET (VOIDmode, + gen_rtx_MEM (DFmode, addr), + gen_rtx_REG (DFmode, i))); + } + } + + /* Set frame pointer, if needed. */ + + if (frame_pointer_needed) + { + insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + } + + /* Set up got pointer, if needed. */ + + if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM)) + { + rtx insns = s390_load_got (); + + for (insn = insns; insn; insn = NEXT_INSN (insn)) + annotate_constant_pool_refs (&PATTERN (insn)); + + emit_insn (insns); + } + + if (TARGET_TPF_PROFILING) + { + /* Generate a BAS instruction to serve as a function + entry intercept to facilitate the use of tracing + algorithms located at the branch target. */ + emit_insn (gen_prologue_tpf ()); + + /* Emit a blockage here so that all code + lies between the profiling mechanisms. */ + emit_insn (gen_blockage ()); + } +} + +/* Expand the epilogue into a bunch of separate insns. */ + +void +s390_emit_epilogue (bool sibcall) +{ + rtx frame_pointer, return_reg, cfa_restores = NULL_RTX; + int area_bottom, area_top, offset = 0; + int next_offset; + rtvec p; + int i; + + if (TARGET_TPF_PROFILING) + { + + /* Generate a BAS instruction to serve as a function + entry intercept to facilitate the use of tracing + algorithms located at the branch target. */ + + /* Emit a blockage here so that all code + lies between the profiling mechanisms. */ + emit_insn (gen_blockage ()); + + emit_insn (gen_epilogue_tpf ()); + } + + /* Check whether to use frame or stack pointer for restore. */ + + frame_pointer = (frame_pointer_needed + ? hard_frame_pointer_rtx : stack_pointer_rtx); + + s390_frame_area (&area_bottom, &area_top); + + /* Check whether we can access the register save area. + If not, increment the frame pointer as required. */ + + if (area_top <= area_bottom) + { + /* Nothing to restore. */ + } + else if (DISP_IN_RANGE (cfun_frame_layout.frame_size + area_bottom) + && DISP_IN_RANGE (cfun_frame_layout.frame_size + area_top - 1)) + { + /* Area is in range. */ + offset = cfun_frame_layout.frame_size; + } + else + { + rtx insn, frame_off, cfa; + + offset = area_bottom < 0 ? -area_bottom : 0; + frame_off = GEN_INT (cfun_frame_layout.frame_size - offset); + + cfa = gen_rtx_SET (VOIDmode, frame_pointer, + gen_rtx_PLUS (Pmode, frame_pointer, frame_off)); + if (DISP_IN_RANGE (INTVAL (frame_off))) + { + insn = gen_rtx_SET (VOIDmode, frame_pointer, + gen_rtx_PLUS (Pmode, frame_pointer, frame_off)); + insn = emit_insn (insn); + } + else + { + if (!CONST_OK_FOR_K (INTVAL (frame_off))) + frame_off = force_const_mem (Pmode, frame_off); + + insn = emit_insn (gen_add2_insn (frame_pointer, frame_off)); + annotate_constant_pool_refs (&PATTERN (insn)); + } + add_reg_note (insn, REG_CFA_ADJUST_CFA, cfa); + RTX_FRAME_RELATED_P (insn) = 1; + } + + /* Restore call saved fprs. */ + + if (TARGET_64BIT) + { + if (cfun_save_high_fprs_p) + { + next_offset = cfun_frame_layout.f8_offset; + for (i = 24; i < 32; i++) + { + if (cfun_fpr_bit_p (i - 16)) + { + restore_fpr (frame_pointer, + offset + next_offset, i); + cfa_restores + = alloc_reg_note (REG_CFA_RESTORE, + gen_rtx_REG (DFmode, i), cfa_restores); + next_offset += 8; + } + } + } + + } + else + { + next_offset = cfun_frame_layout.f4_offset; + for (i = 18; i < 20; i++) + { + if (cfun_fpr_bit_p (i - 16)) + { + restore_fpr (frame_pointer, + offset + next_offset, i); + cfa_restores + = alloc_reg_note (REG_CFA_RESTORE, + gen_rtx_REG (DFmode, i), cfa_restores); + next_offset += 8; + } + else if (!TARGET_PACKED_STACK) + next_offset += 8; + } + + } + + /* Return register. */ + + return_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); + + /* Restore call saved gprs. */ + + if (cfun_frame_layout.first_restore_gpr != -1) + { + rtx insn, addr; + int i; + + /* Check for global register and save them + to stack location from where they get restored. */ + + for (i = cfun_frame_layout.first_restore_gpr; + i <= cfun_frame_layout.last_restore_gpr; + i++) + { + if (global_not_special_regno_p (i)) + { + addr = plus_constant (frame_pointer, + offset + cfun_frame_layout.gprs_offset + + (i - cfun_frame_layout.first_save_gpr_slot) + * UNITS_PER_LONG); + addr = gen_rtx_MEM (Pmode, addr); + set_mem_alias_set (addr, get_frame_alias_set ()); + emit_move_insn (addr, gen_rtx_REG (Pmode, i)); + } + else + cfa_restores + = alloc_reg_note (REG_CFA_RESTORE, + gen_rtx_REG (Pmode, i), cfa_restores); + } + + if (! sibcall) + { + /* Fetch return address from stack before load multiple, + this will do good for scheduling. */ + + if (cfun_frame_layout.save_return_addr_p + || (cfun_frame_layout.first_restore_gpr < BASE_REGNUM + && cfun_frame_layout.last_restore_gpr > RETURN_REGNUM)) + { + int return_regnum = find_unused_clobbered_reg(); + if (!return_regnum) + return_regnum = 4; + return_reg = gen_rtx_REG (Pmode, return_regnum); + + addr = plus_constant (frame_pointer, + offset + cfun_frame_layout.gprs_offset + + (RETURN_REGNUM + - cfun_frame_layout.first_save_gpr_slot) + * UNITS_PER_LONG); + addr = gen_rtx_MEM (Pmode, addr); + set_mem_alias_set (addr, get_frame_alias_set ()); + emit_move_insn (return_reg, addr); + } + } + + insn = restore_gprs (frame_pointer, + offset + cfun_frame_layout.gprs_offset + + (cfun_frame_layout.first_restore_gpr + - cfun_frame_layout.first_save_gpr_slot) + * UNITS_PER_LONG, + cfun_frame_layout.first_restore_gpr, + cfun_frame_layout.last_restore_gpr); + insn = emit_insn (insn); + REG_NOTES (insn) = cfa_restores; + add_reg_note (insn, REG_CFA_DEF_CFA, + plus_constant (stack_pointer_rtx, STACK_POINTER_OFFSET)); + RTX_FRAME_RELATED_P (insn) = 1; + } + + if (! sibcall) + { + + /* Return to caller. */ + + p = rtvec_alloc (2); + + RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode); + RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg); + emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p)); + } +} + + +/* Return the size in bytes of a function argument of + type TYPE and/or mode MODE. At least one of TYPE or + MODE must be specified. */ + +static int +s390_function_arg_size (enum machine_mode mode, const_tree type) +{ + if (type) + return int_size_in_bytes (type); + + /* No type info available for some library calls ... */ + if (mode != BLKmode) + return GET_MODE_SIZE (mode); + + /* If we have neither type nor mode, abort */ + gcc_unreachable (); +} + +/* Return true if a function argument of type TYPE and mode MODE + is to be passed in a floating-point register, if available. */ + +static bool +s390_function_arg_float (enum machine_mode mode, const_tree type) +{ + int size = s390_function_arg_size (mode, type); + if (size > 8) + return false; + + /* Soft-float changes the ABI: no floating-point registers are used. */ + if (TARGET_SOFT_FLOAT) + return false; + + /* No type info available for some library calls ... */ + if (!type) + return mode == SFmode || mode == DFmode || mode == SDmode || mode == DDmode; + + /* The ABI says that record types with a single member are treated + just like that member would be. */ + while (TREE_CODE (type) == RECORD_TYPE) + { + tree field, single = NULL_TREE; + + for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) != FIELD_DECL) + continue; + + if (single == NULL_TREE) + single = TREE_TYPE (field); + else + return false; + } + + if (single == NULL_TREE) + return false; + else + type = single; + } + + return TREE_CODE (type) == REAL_TYPE; +} + +/* Return true if a function argument of type TYPE and mode MODE + is to be passed in an integer register, or a pair of integer + registers, if available. */ + +static bool +s390_function_arg_integer (enum machine_mode mode, const_tree type) +{ + int size = s390_function_arg_size (mode, type); + if (size > 8) + return false; + + /* No type info available for some library calls ... */ + if (!type) + return GET_MODE_CLASS (mode) == MODE_INT + || (TARGET_SOFT_FLOAT && SCALAR_FLOAT_MODE_P (mode)); + + /* We accept small integral (and similar) types. */ + if (INTEGRAL_TYPE_P (type) + || POINTER_TYPE_P (type) + || TREE_CODE (type) == NULLPTR_TYPE + || TREE_CODE (type) == OFFSET_TYPE + || (TARGET_SOFT_FLOAT && TREE_CODE (type) == REAL_TYPE)) + return true; + + /* We also accept structs of size 1, 2, 4, 8 that are not + passed in floating-point registers. */ + if (AGGREGATE_TYPE_P (type) + && exact_log2 (size) >= 0 + && !s390_function_arg_float (mode, type)) + return true; + + return false; +} + +/* Return 1 if a function argument of type TYPE and mode MODE + is to be passed by reference. The ABI specifies that only + structures of size 1, 2, 4, or 8 bytes are passed by value, + all other structures (and complex numbers) are passed by + reference. */ + +static bool +s390_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED, + enum machine_mode mode, const_tree type, + bool named ATTRIBUTE_UNUSED) +{ + int size = s390_function_arg_size (mode, type); + if (size > 8) + return true; + + if (type) + { + if (AGGREGATE_TYPE_P (type) && exact_log2 (size) < 0) + return 1; + + if (TREE_CODE (type) == COMPLEX_TYPE + || TREE_CODE (type) == VECTOR_TYPE) + return 1; + } + + return 0; +} + +/* Update the data in CUM to advance over an argument of mode MODE and + data type TYPE. (TYPE is null for libcalls where that information + may not be available.). The boolean NAMED specifies whether the + argument is a named argument (as opposed to an unnamed argument + matching an ellipsis). */ + +static void +s390_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, + const_tree type, bool named ATTRIBUTE_UNUSED) +{ + if (s390_function_arg_float (mode, type)) + { + cum->fprs += 1; + } + else if (s390_function_arg_integer (mode, type)) + { + int size = s390_function_arg_size (mode, type); + cum->gprs += ((size + UNITS_PER_LONG - 1) / UNITS_PER_LONG); + } + else + gcc_unreachable (); +} + +/* Define where to put the arguments to a function. + Value is zero to push the argument on the stack, + or a hard register in which to store the argument. + + MODE is the argument's machine mode. + TYPE is the data type of the argument (as a tree). + This is null for libcalls where that information may + not be available. + CUM is a variable of type CUMULATIVE_ARGS which gives info about + the preceding args and about the function being called. + NAMED is nonzero if this argument is a named parameter + (otherwise it is an extra parameter matching an ellipsis). + + On S/390, we use general purpose registers 2 through 6 to + pass integer, pointer, and certain structure arguments, and + floating point registers 0 and 2 (0, 2, 4, and 6 on 64-bit) + to pass floating point arguments. All remaining arguments + are pushed to the stack. */ + +static rtx +s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, + const_tree type, bool named ATTRIBUTE_UNUSED) +{ + if (s390_function_arg_float (mode, type)) + { + if (cum->fprs + 1 > FP_ARG_NUM_REG) + return 0; + else + return gen_rtx_REG (mode, cum->fprs + 16); + } + else if (s390_function_arg_integer (mode, type)) + { + int size = s390_function_arg_size (mode, type); + int n_gprs = (size + UNITS_PER_LONG - 1) / UNITS_PER_LONG; + + if (cum->gprs + n_gprs > GP_ARG_NUM_REG) + return 0; + else if (n_gprs == 1 || UNITS_PER_WORD == UNITS_PER_LONG) + return gen_rtx_REG (mode, cum->gprs + 2); + else if (n_gprs == 2) + { + rtvec p = rtvec_alloc (2); + + RTVEC_ELT (p, 0) + = gen_rtx_EXPR_LIST (SImode, gen_rtx_REG (SImode, cum->gprs + 2), + const0_rtx); + RTVEC_ELT (p, 1) + = gen_rtx_EXPR_LIST (SImode, gen_rtx_REG (SImode, cum->gprs + 3), + GEN_INT (4)); + + return gen_rtx_PARALLEL (mode, p); + } + } + + /* After the real arguments, expand_call calls us once again + with a void_type_node type. Whatever we return here is + passed as operand 2 to the call expanders. + + We don't need this feature ... */ + else if (type == void_type_node) + return const0_rtx; + + gcc_unreachable (); +} + +/* Return true if return values of type TYPE should be returned + in a memory buffer whose address is passed by the caller as + hidden first argument. */ + +static bool +s390_return_in_memory (const_tree type, const_tree fundecl ATTRIBUTE_UNUSED) +{ + /* We accept small integral (and similar) types. */ + if (INTEGRAL_TYPE_P (type) + || POINTER_TYPE_P (type) + || TREE_CODE (type) == OFFSET_TYPE + || TREE_CODE (type) == REAL_TYPE) + return int_size_in_bytes (type) > 8; + + /* Aggregates and similar constructs are always returned + in memory. */ + if (AGGREGATE_TYPE_P (type) + || TREE_CODE (type) == COMPLEX_TYPE + || TREE_CODE (type) == VECTOR_TYPE) + return true; + + /* ??? We get called on all sorts of random stuff from + aggregate_value_p. We can't abort, but it's not clear + what's safe to return. Pretend it's a struct I guess. */ + return true; +} + +/* Function arguments and return values are promoted to word size. */ + +static enum machine_mode +s390_promote_function_mode (const_tree type, enum machine_mode mode, + int *punsignedp, + const_tree fntype ATTRIBUTE_UNUSED, + int for_return ATTRIBUTE_UNUSED) +{ + if (INTEGRAL_MODE_P (mode) + && GET_MODE_SIZE (mode) < UNITS_PER_LONG) + { + if (POINTER_TYPE_P (type)) + *punsignedp = POINTERS_EXTEND_UNSIGNED; + return Pmode; + } + + return mode; +} + +/* Define where to return a (scalar) value of type RET_TYPE. + If RET_TYPE is null, define where to return a (scalar) + value of mode MODE from a libcall. */ + +static rtx +s390_function_and_libcall_value (enum machine_mode mode, + const_tree ret_type, + const_tree fntype_or_decl, + bool outgoing ATTRIBUTE_UNUSED) +{ + /* For normal functions perform the promotion as + promote_function_mode would do. */ + if (ret_type) + { + int unsignedp = TYPE_UNSIGNED (ret_type); + mode = promote_function_mode (ret_type, mode, &unsignedp, + fntype_or_decl, 1); + } + + gcc_assert (GET_MODE_CLASS (mode) == MODE_INT || SCALAR_FLOAT_MODE_P (mode)); + gcc_assert (GET_MODE_SIZE (mode) <= 8); + + if (TARGET_HARD_FLOAT && SCALAR_FLOAT_MODE_P (mode)) + return gen_rtx_REG (mode, 16); + else if (GET_MODE_SIZE (mode) <= UNITS_PER_LONG + || UNITS_PER_LONG == UNITS_PER_WORD) + return gen_rtx_REG (mode, 2); + else if (GET_MODE_SIZE (mode) == 2 * UNITS_PER_LONG) + { + /* This case is triggered when returning a 64 bit value with + -m31 -mzarch. Although the value would fit into a single + register it has to be forced into a 32 bit register pair in + order to match the ABI. */ + rtvec p = rtvec_alloc (2); + + RTVEC_ELT (p, 0) + = gen_rtx_EXPR_LIST (SImode, gen_rtx_REG (SImode, 2), const0_rtx); + RTVEC_ELT (p, 1) + = gen_rtx_EXPR_LIST (SImode, gen_rtx_REG (SImode, 3), GEN_INT (4)); + + return gen_rtx_PARALLEL (mode, p); + } + + gcc_unreachable (); +} + +/* Define where to return a scalar return value of type RET_TYPE. */ + +static rtx +s390_function_value (const_tree ret_type, const_tree fn_decl_or_type, + bool outgoing) +{ + return s390_function_and_libcall_value (TYPE_MODE (ret_type), ret_type, + fn_decl_or_type, outgoing); +} + +/* Define where to return a scalar libcall return value of mode + MODE. */ + +static rtx +s390_libcall_value (enum machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED) +{ + return s390_function_and_libcall_value (mode, NULL_TREE, + NULL_TREE, true); +} + + +/* Create and return the va_list datatype. + + On S/390, va_list is an array type equivalent to + + typedef struct __va_list_tag + { + long __gpr; + long __fpr; + void *__overflow_arg_area; + void *__reg_save_area; + } va_list[1]; + + where __gpr and __fpr hold the number of general purpose + or floating point arguments used up to now, respectively, + __overflow_arg_area points to the stack location of the + next argument passed on the stack, and __reg_save_area + always points to the start of the register area in the + call frame of the current function. The function prologue + saves all registers used for argument passing into this + area if the function uses variable arguments. */ + +static tree +s390_build_builtin_va_list (void) +{ + tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl; + + record = lang_hooks.types.make_type (RECORD_TYPE); + + type_decl = + build_decl (BUILTINS_LOCATION, + TYPE_DECL, get_identifier ("__va_list_tag"), record); + + f_gpr = build_decl (BUILTINS_LOCATION, + FIELD_DECL, get_identifier ("__gpr"), + long_integer_type_node); + f_fpr = build_decl (BUILTINS_LOCATION, + FIELD_DECL, get_identifier ("__fpr"), + long_integer_type_node); + f_ovf = build_decl (BUILTINS_LOCATION, + FIELD_DECL, get_identifier ("__overflow_arg_area"), + ptr_type_node); + f_sav = build_decl (BUILTINS_LOCATION, + FIELD_DECL, get_identifier ("__reg_save_area"), + ptr_type_node); + + va_list_gpr_counter_field = f_gpr; + va_list_fpr_counter_field = f_fpr; + + DECL_FIELD_CONTEXT (f_gpr) = record; + DECL_FIELD_CONTEXT (f_fpr) = record; + DECL_FIELD_CONTEXT (f_ovf) = record; + DECL_FIELD_CONTEXT (f_sav) = record; + + TYPE_STUB_DECL (record) = type_decl; + TYPE_NAME (record) = type_decl; + TYPE_FIELDS (record) = f_gpr; + DECL_CHAIN (f_gpr) = f_fpr; + DECL_CHAIN (f_fpr) = f_ovf; + DECL_CHAIN (f_ovf) = f_sav; + + layout_type (record); + + /* The correct type is an array type of one element. */ + return build_array_type (record, build_index_type (size_zero_node)); +} + +/* Implement va_start by filling the va_list structure VALIST. + STDARG_P is always true, and ignored. + NEXTARG points to the first anonymous stack argument. + + The following global variables are used to initialize + the va_list structure: + + crtl->args.info: + holds number of gprs and fprs used for named arguments. + crtl->args.arg_offset_rtx: + holds the offset of the first anonymous stack argument + (relative to the virtual arg pointer). */ + +static void +s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED) +{ + HOST_WIDE_INT n_gpr, n_fpr; + int off; + tree f_gpr, f_fpr, f_ovf, f_sav; + tree gpr, fpr, ovf, sav, t; + + f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); + f_fpr = DECL_CHAIN (f_gpr); + f_ovf = DECL_CHAIN (f_fpr); + f_sav = DECL_CHAIN (f_ovf); + + valist = build_simple_mem_ref (valist); + gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE); + fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE); + ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE); + sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE); + + /* Count number of gp and fp argument registers used. */ + + n_gpr = crtl->args.info.gprs; + n_fpr = crtl->args.info.fprs; + + if (cfun->va_list_gpr_size) + { + t = build2 (MODIFY_EXPR, TREE_TYPE (gpr), gpr, + build_int_cst (NULL_TREE, n_gpr)); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + } + + if (cfun->va_list_fpr_size) + { + t = build2 (MODIFY_EXPR, TREE_TYPE (fpr), fpr, + build_int_cst (NULL_TREE, n_fpr)); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + } + + /* Find the overflow area. */ + if (n_gpr + cfun->va_list_gpr_size > GP_ARG_NUM_REG + || n_fpr + cfun->va_list_fpr_size > FP_ARG_NUM_REG) + { + t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx); + + off = INTVAL (crtl->args.arg_offset_rtx); + off = off < 0 ? 0 : off; + if (TARGET_DEBUG_ARG) + fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n", + (int)n_gpr, (int)n_fpr, off); + + t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovf), t, size_int (off)); + + t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + } + + /* Find the register save area. */ + if ((cfun->va_list_gpr_size && n_gpr < GP_ARG_NUM_REG) + || (cfun->va_list_fpr_size && n_fpr < FP_ARG_NUM_REG)) + { + t = make_tree (TREE_TYPE (sav), return_address_pointer_rtx); + t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (sav), t, + size_int (-RETURN_REGNUM * UNITS_PER_LONG)); + + t = build2 (MODIFY_EXPR, TREE_TYPE (sav), sav, t); + TREE_SIDE_EFFECTS (t) = 1; + expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL); + } +} + +/* Implement va_arg by updating the va_list structure + VALIST as required to retrieve an argument of type + TYPE, and returning that argument. + + Generates code equivalent to: + + if (integral value) { + if (size <= 4 && args.gpr < 5 || + size > 4 && args.gpr < 4 ) + ret = args.reg_save_area[args.gpr+8] + else + ret = *args.overflow_arg_area++; + } else if (float value) { + if (args.fgpr < 2) + ret = args.reg_save_area[args.fpr+64] + else + ret = *args.overflow_arg_area++; + } else if (aggregate value) { + if (args.gpr < 5) + ret = *args.reg_save_area[args.gpr] + else + ret = **args.overflow_arg_area++; + } */ + +static tree +s390_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p, + gimple_seq *post_p ATTRIBUTE_UNUSED) +{ + tree f_gpr, f_fpr, f_ovf, f_sav; + tree gpr, fpr, ovf, sav, reg, t, u; + int indirect_p, size, n_reg, sav_ofs, sav_scale, max_reg; + tree lab_false, lab_over, addr; + + f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node)); + f_fpr = DECL_CHAIN (f_gpr); + f_ovf = DECL_CHAIN (f_fpr); + f_sav = DECL_CHAIN (f_ovf); + + valist = build_va_arg_indirect_ref (valist); + gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE); + fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE); + sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE); + + /* The tree for args* cannot be shared between gpr/fpr and ovf since + both appear on a lhs. */ + valist = unshare_expr (valist); + ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE); + + size = int_size_in_bytes (type); + + if (pass_by_reference (NULL, TYPE_MODE (type), type, false)) + { + if (TARGET_DEBUG_ARG) + { + fprintf (stderr, "va_arg: aggregate type"); + debug_tree (type); + } + + /* Aggregates are passed by reference. */ + indirect_p = 1; + reg = gpr; + n_reg = 1; + + /* kernel stack layout on 31 bit: It is assumed here that no padding + will be added by s390_frame_info because for va_args always an even + number of gprs has to be saved r15-r2 = 14 regs. */ + sav_ofs = 2 * UNITS_PER_LONG; + sav_scale = UNITS_PER_LONG; + size = UNITS_PER_LONG; + max_reg = GP_ARG_NUM_REG - n_reg; + } + else if (s390_function_arg_float (TYPE_MODE (type), type)) + { + if (TARGET_DEBUG_ARG) + { + fprintf (stderr, "va_arg: float type"); + debug_tree (type); + } + + /* FP args go in FP registers, if present. */ + indirect_p = 0; + reg = fpr; + n_reg = 1; + sav_ofs = 16 * UNITS_PER_LONG; + sav_scale = 8; + max_reg = FP_ARG_NUM_REG - n_reg; + } + else + { + if (TARGET_DEBUG_ARG) + { + fprintf (stderr, "va_arg: other type"); + debug_tree (type); + } + + /* Otherwise into GP registers. */ + indirect_p = 0; + reg = gpr; + n_reg = (size + UNITS_PER_LONG - 1) / UNITS_PER_LONG; + + /* kernel stack layout on 31 bit: It is assumed here that no padding + will be added by s390_frame_info because for va_args always an even + number of gprs has to be saved r15-r2 = 14 regs. */ + sav_ofs = 2 * UNITS_PER_LONG; + + if (size < UNITS_PER_LONG) + sav_ofs += UNITS_PER_LONG - size; + + sav_scale = UNITS_PER_LONG; + max_reg = GP_ARG_NUM_REG - n_reg; + } + + /* Pull the value out of the saved registers ... */ + + lab_false = create_artificial_label (UNKNOWN_LOCATION); + lab_over = create_artificial_label (UNKNOWN_LOCATION); + addr = create_tmp_var (ptr_type_node, "addr"); + + t = fold_convert (TREE_TYPE (reg), size_int (max_reg)); + t = build2 (GT_EXPR, boolean_type_node, reg, t); + u = build1 (GOTO_EXPR, void_type_node, lab_false); + t = build3 (COND_EXPR, void_type_node, t, u, NULL_TREE); + gimplify_and_add (t, pre_p); + + t = build2 (POINTER_PLUS_EXPR, ptr_type_node, sav, + size_int (sav_ofs)); + u = build2 (MULT_EXPR, TREE_TYPE (reg), reg, + fold_convert (TREE_TYPE (reg), size_int (sav_scale))); + t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, fold_convert (sizetype, u)); + + gimplify_assign (addr, t, pre_p); + + gimple_seq_add_stmt (pre_p, gimple_build_goto (lab_over)); + + gimple_seq_add_stmt (pre_p, gimple_build_label (lab_false)); + + + /* ... Otherwise out of the overflow area. */ + + t = ovf; + if (size < UNITS_PER_LONG) + t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, + size_int (UNITS_PER_LONG - size)); + + gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue); + + gimplify_assign (addr, t, pre_p); + + t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, + size_int (size)); + gimplify_assign (ovf, t, pre_p); + + gimple_seq_add_stmt (pre_p, gimple_build_label (lab_over)); + + + /* Increment register save count. */ + + u = build2 (PREINCREMENT_EXPR, TREE_TYPE (reg), reg, + fold_convert (TREE_TYPE (reg), size_int (n_reg))); + gimplify_and_add (u, pre_p); + + if (indirect_p) + { + t = build_pointer_type_for_mode (build_pointer_type (type), + ptr_mode, true); + addr = fold_convert (t, addr); + addr = build_va_arg_indirect_ref (addr); + } + else + { + t = build_pointer_type_for_mode (type, ptr_mode, true); + addr = fold_convert (t, addr); + } + + return build_va_arg_indirect_ref (addr); +} + + +/* Builtins. */ + +enum s390_builtin +{ + S390_BUILTIN_THREAD_POINTER, + S390_BUILTIN_SET_THREAD_POINTER, + + S390_BUILTIN_max +}; + +static enum insn_code const code_for_builtin_64[S390_BUILTIN_max] = { + CODE_FOR_get_tp_64, + CODE_FOR_set_tp_64 +}; + +static enum insn_code const code_for_builtin_31[S390_BUILTIN_max] = { + CODE_FOR_get_tp_31, + CODE_FOR_set_tp_31 +}; + +static void +s390_init_builtins (void) +{ + tree ftype; + + ftype = build_function_type (ptr_type_node, void_list_node); + add_builtin_function ("__builtin_thread_pointer", ftype, + S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD, + NULL, NULL_TREE); + + ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE); + add_builtin_function ("__builtin_set_thread_pointer", ftype, + S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD, + NULL, NULL_TREE); +} + +/* Expand an expression EXP that calls a built-in function, + with result going to TARGET if that's convenient + (and in mode MODE if that's convenient). + SUBTARGET may be used as the target for computing one of EXP's operands. + IGNORE is nonzero if the value is to be ignored. */ + +static rtx +s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, + enum machine_mode mode ATTRIBUTE_UNUSED, + int ignore ATTRIBUTE_UNUSED) +{ +#define MAX_ARGS 2 + + enum insn_code const *code_for_builtin = + TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31; + + tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); + unsigned int fcode = DECL_FUNCTION_CODE (fndecl); + enum insn_code icode; + rtx op[MAX_ARGS], pat; + int arity; + bool nonvoid; + tree arg; + call_expr_arg_iterator iter; + + if (fcode >= S390_BUILTIN_max) + internal_error ("bad builtin fcode"); + icode = code_for_builtin[fcode]; + if (icode == 0) + internal_error ("bad builtin fcode"); + + nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node; + + arity = 0; + FOR_EACH_CALL_EXPR_ARG (arg, iter, exp) + { + const struct insn_operand_data *insn_op; + + if (arg == error_mark_node) + return NULL_RTX; + if (arity > MAX_ARGS) + return NULL_RTX; + + insn_op = &insn_data[icode].operand[arity + nonvoid]; + + op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, EXPAND_NORMAL); + + if (!(*insn_op->predicate) (op[arity], insn_op->mode)) + op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]); + arity++; + } + + if (nonvoid) + { + enum machine_mode tmode = insn_data[icode].operand[0].mode; + if (!target + || GET_MODE (target) != tmode + || !(*insn_data[icode].operand[0].predicate) (target, tmode)) + target = gen_reg_rtx (tmode); + } + + switch (arity) + { + case 0: + pat = GEN_FCN (icode) (target); + break; + case 1: + if (nonvoid) + pat = GEN_FCN (icode) (target, op[0]); + else + pat = GEN_FCN (icode) (op[0]); + break; + case 2: + pat = GEN_FCN (icode) (target, op[0], op[1]); + break; + default: + gcc_unreachable (); + } + if (!pat) + return NULL_RTX; + emit_insn (pat); + + if (nonvoid) + return target; + else + return const0_rtx; +} + + +/* Output assembly code for the trampoline template to + stdio stream FILE. + + On S/390, we use gpr 1 internally in the trampoline code; + gpr 0 is used to hold the static chain. */ + +static void +s390_asm_trampoline_template (FILE *file) +{ + rtx op[2]; + op[0] = gen_rtx_REG (Pmode, 0); + op[1] = gen_rtx_REG (Pmode, 1); + + if (TARGET_64BIT) + { + output_asm_insn ("basr\t%1,0", op); /* 2 byte */ + output_asm_insn ("lmg\t%0,%1,14(%1)", op); /* 6 byte */ + output_asm_insn ("br\t%1", op); /* 2 byte */ + ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 10)); + } + else + { + output_asm_insn ("basr\t%1,0", op); /* 2 byte */ + output_asm_insn ("lm\t%0,%1,6(%1)", op); /* 4 byte */ + output_asm_insn ("br\t%1", op); /* 2 byte */ + ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 8)); + } +} + +/* Emit RTL insns to initialize the variable parts of a trampoline. + FNADDR is an RTX for the address of the function's pure code. + CXT is an RTX for the static chain value for the function. */ + +static void +s390_trampoline_init (rtx m_tramp, tree fndecl, rtx cxt) +{ + rtx fnaddr = XEXP (DECL_RTL (fndecl), 0); + rtx mem; + + emit_block_move (m_tramp, assemble_trampoline_template (), + GEN_INT (2 * UNITS_PER_LONG), BLOCK_OP_NORMAL); + + mem = adjust_address (m_tramp, Pmode, 2 * UNITS_PER_LONG); + emit_move_insn (mem, cxt); + mem = adjust_address (m_tramp, Pmode, 3 * UNITS_PER_LONG); + emit_move_insn (mem, fnaddr); +} + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. */ + +void +s390_function_profiler (FILE *file, int labelno) +{ + rtx op[7]; + + char label[128]; + ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno); + + fprintf (file, "# function profiler \n"); + + op[0] = gen_rtx_REG (Pmode, RETURN_REGNUM); + op[1] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); + op[1] = gen_rtx_MEM (Pmode, plus_constant (op[1], UNITS_PER_LONG)); + + op[2] = gen_rtx_REG (Pmode, 1); + op[3] = gen_rtx_SYMBOL_REF (Pmode, label); + SYMBOL_REF_FLAGS (op[3]) = SYMBOL_FLAG_LOCAL; + + op[4] = gen_rtx_SYMBOL_REF (Pmode, "_mcount"); + if (flag_pic) + { + op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), UNSPEC_PLT); + op[4] = gen_rtx_CONST (Pmode, op[4]); + } + + if (TARGET_64BIT) + { + output_asm_insn ("stg\t%0,%1", op); + output_asm_insn ("larl\t%2,%3", op); + output_asm_insn ("brasl\t%0,%4", op); + output_asm_insn ("lg\t%0,%1", op); + } + else if (!flag_pic) + { + op[6] = gen_label_rtx (); + + output_asm_insn ("st\t%0,%1", op); + output_asm_insn ("bras\t%2,%l6", op); + output_asm_insn (".long\t%4", op); + output_asm_insn (".long\t%3", op); + targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6])); + output_asm_insn ("l\t%0,0(%2)", op); + output_asm_insn ("l\t%2,4(%2)", op); + output_asm_insn ("basr\t%0,%0", op); + output_asm_insn ("l\t%0,%1", op); + } + else + { + op[5] = gen_label_rtx (); + op[6] = gen_label_rtx (); + + output_asm_insn ("st\t%0,%1", op); + output_asm_insn ("bras\t%2,%l6", op); + targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[5])); + output_asm_insn (".long\t%4-%l5", op); + output_asm_insn (".long\t%3-%l5", op); + targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6])); + output_asm_insn ("lr\t%0,%2", op); + output_asm_insn ("a\t%0,0(%2)", op); + output_asm_insn ("a\t%2,4(%2)", op); + output_asm_insn ("basr\t%0,%0", op); + output_asm_insn ("l\t%0,%1", op); + } +} + +/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF + into its SYMBOL_REF_FLAGS. */ + +static void +s390_encode_section_info (tree decl, rtx rtl, int first) +{ + default_encode_section_info (decl, rtl, first); + + if (TREE_CODE (decl) == VAR_DECL) + { + /* If a variable has a forced alignment to < 2 bytes, mark it + with SYMBOL_FLAG_ALIGN1 to prevent it from being used as LARL + operand. */ + if (DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 16) + SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_ALIGN1; + if (!DECL_SIZE (decl) + || !DECL_ALIGN (decl) + || !host_integerp (DECL_SIZE (decl), 0) + || (DECL_ALIGN (decl) <= 64 + && DECL_ALIGN (decl) != tree_low_cst (DECL_SIZE (decl), 0))) + SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_NOT_NATURALLY_ALIGNED; + } + + /* Literal pool references don't have a decl so they are handled + differently here. We rely on the information in the MEM_ALIGN + entry to decide upon natural alignment. */ + if (MEM_P (rtl) + && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF + && TREE_CONSTANT_POOL_ADDRESS_P (XEXP (rtl, 0)) + && (MEM_ALIGN (rtl) == 0 + || GET_MODE_BITSIZE (GET_MODE (rtl)) == 0 + || MEM_ALIGN (rtl) < GET_MODE_BITSIZE (GET_MODE (rtl)))) + SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_NOT_NATURALLY_ALIGNED; +} + +/* Output thunk to FILE that implements a C++ virtual function call (with + multiple inheritance) to FUNCTION. The thunk adjusts the this pointer + by DELTA, and unless VCALL_OFFSET is zero, applies an additional adjustment + stored at VCALL_OFFSET in the vtable whose address is located at offset 0 + relative to the resulting this pointer. */ + +static void +s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, + tree function) +{ + rtx op[10]; + int nonlocal = 0; + + /* Make sure unwind info is emitted for the thunk if needed. */ + final_start_function (emit_barrier (), file, 1); + + /* Operand 0 is the target function. */ + op[0] = XEXP (DECL_RTL (function), 0); + if (flag_pic && !SYMBOL_REF_LOCAL_P (op[0])) + { + nonlocal = 1; + op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]), + TARGET_64BIT ? UNSPEC_PLT : UNSPEC_GOT); + op[0] = gen_rtx_CONST (Pmode, op[0]); + } + + /* Operand 1 is the 'this' pointer. */ + if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) + op[1] = gen_rtx_REG (Pmode, 3); + else + op[1] = gen_rtx_REG (Pmode, 2); + + /* Operand 2 is the delta. */ + op[2] = GEN_INT (delta); + + /* Operand 3 is the vcall_offset. */ + op[3] = GEN_INT (vcall_offset); + + /* Operand 4 is the temporary register. */ + op[4] = gen_rtx_REG (Pmode, 1); + + /* Operands 5 to 8 can be used as labels. */ + op[5] = NULL_RTX; + op[6] = NULL_RTX; + op[7] = NULL_RTX; + op[8] = NULL_RTX; + + /* Operand 9 can be used for temporary register. */ + op[9] = NULL_RTX; + + /* Generate code. */ + if (TARGET_64BIT) + { + /* Setup literal pool pointer if required. */ + if ((!DISP_IN_RANGE (delta) + && !CONST_OK_FOR_K (delta) + && !CONST_OK_FOR_Os (delta)) + || (!DISP_IN_RANGE (vcall_offset) + && !CONST_OK_FOR_K (vcall_offset) + && !CONST_OK_FOR_Os (vcall_offset))) + { + op[5] = gen_label_rtx (); + output_asm_insn ("larl\t%4,%5", op); + } + + /* Add DELTA to this pointer. */ + if (delta) + { + if (CONST_OK_FOR_J (delta)) + output_asm_insn ("la\t%1,%2(%1)", op); + else if (DISP_IN_RANGE (delta)) + output_asm_insn ("lay\t%1,%2(%1)", op); + else if (CONST_OK_FOR_K (delta)) + output_asm_insn ("aghi\t%1,%2", op); + else if (CONST_OK_FOR_Os (delta)) + output_asm_insn ("agfi\t%1,%2", op); + else + { + op[6] = gen_label_rtx (); + output_asm_insn ("agf\t%1,%6-%5(%4)", op); + } + } + + /* Perform vcall adjustment. */ + if (vcall_offset) + { + if (DISP_IN_RANGE (vcall_offset)) + { + output_asm_insn ("lg\t%4,0(%1)", op); + output_asm_insn ("ag\t%1,%3(%4)", op); + } + else if (CONST_OK_FOR_K (vcall_offset)) + { + output_asm_insn ("lghi\t%4,%3", op); + output_asm_insn ("ag\t%4,0(%1)", op); + output_asm_insn ("ag\t%1,0(%4)", op); + } + else if (CONST_OK_FOR_Os (vcall_offset)) + { + output_asm_insn ("lgfi\t%4,%3", op); + output_asm_insn ("ag\t%4,0(%1)", op); + output_asm_insn ("ag\t%1,0(%4)", op); + } + else + { + op[7] = gen_label_rtx (); + output_asm_insn ("llgf\t%4,%7-%5(%4)", op); + output_asm_insn ("ag\t%4,0(%1)", op); + output_asm_insn ("ag\t%1,0(%4)", op); + } + } + + /* Jump to target. */ + output_asm_insn ("jg\t%0", op); + + /* Output literal pool if required. */ + if (op[5]) + { + output_asm_insn (".align\t4", op); + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[5])); + } + if (op[6]) + { + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[6])); + output_asm_insn (".long\t%2", op); + } + if (op[7]) + { + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[7])); + output_asm_insn (".long\t%3", op); + } + } + else + { + /* Setup base pointer if required. */ + if (!vcall_offset + || (!DISP_IN_RANGE (delta) + && !CONST_OK_FOR_K (delta) + && !CONST_OK_FOR_Os (delta)) + || (!DISP_IN_RANGE (delta) + && !CONST_OK_FOR_K (vcall_offset) + && !CONST_OK_FOR_Os (vcall_offset))) + { + op[5] = gen_label_rtx (); + output_asm_insn ("basr\t%4,0", op); + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[5])); + } + + /* Add DELTA to this pointer. */ + if (delta) + { + if (CONST_OK_FOR_J (delta)) + output_asm_insn ("la\t%1,%2(%1)", op); + else if (DISP_IN_RANGE (delta)) + output_asm_insn ("lay\t%1,%2(%1)", op); + else if (CONST_OK_FOR_K (delta)) + output_asm_insn ("ahi\t%1,%2", op); + else if (CONST_OK_FOR_Os (delta)) + output_asm_insn ("afi\t%1,%2", op); + else + { + op[6] = gen_label_rtx (); + output_asm_insn ("a\t%1,%6-%5(%4)", op); + } + } + + /* Perform vcall adjustment. */ + if (vcall_offset) + { + if (CONST_OK_FOR_J (vcall_offset)) + { + output_asm_insn ("l\t%4,0(%1)", op); + output_asm_insn ("a\t%1,%3(%4)", op); + } + else if (DISP_IN_RANGE (vcall_offset)) + { + output_asm_insn ("l\t%4,0(%1)", op); + output_asm_insn ("ay\t%1,%3(%4)", op); + } + else if (CONST_OK_FOR_K (vcall_offset)) + { + output_asm_insn ("lhi\t%4,%3", op); + output_asm_insn ("a\t%4,0(%1)", op); + output_asm_insn ("a\t%1,0(%4)", op); + } + else if (CONST_OK_FOR_Os (vcall_offset)) + { + output_asm_insn ("iilf\t%4,%3", op); + output_asm_insn ("a\t%4,0(%1)", op); + output_asm_insn ("a\t%1,0(%4)", op); + } + else + { + op[7] = gen_label_rtx (); + output_asm_insn ("l\t%4,%7-%5(%4)", op); + output_asm_insn ("a\t%4,0(%1)", op); + output_asm_insn ("a\t%1,0(%4)", op); + } + + /* We had to clobber the base pointer register. + Re-setup the base pointer (with a different base). */ + op[5] = gen_label_rtx (); + output_asm_insn ("basr\t%4,0", op); + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[5])); + } + + /* Jump to target. */ + op[8] = gen_label_rtx (); + + if (!flag_pic) + output_asm_insn ("l\t%4,%8-%5(%4)", op); + else if (!nonlocal) + output_asm_insn ("a\t%4,%8-%5(%4)", op); + /* We cannot call through .plt, since .plt requires %r12 loaded. */ + else if (flag_pic == 1) + { + output_asm_insn ("a\t%4,%8-%5(%4)", op); + output_asm_insn ("l\t%4,%0(%4)", op); + } + else if (flag_pic == 2) + { + op[9] = gen_rtx_REG (Pmode, 0); + output_asm_insn ("l\t%9,%8-4-%5(%4)", op); + output_asm_insn ("a\t%4,%8-%5(%4)", op); + output_asm_insn ("ar\t%4,%9", op); + output_asm_insn ("l\t%4,0(%4)", op); + } + + output_asm_insn ("br\t%4", op); + + /* Output literal pool. */ + output_asm_insn (".align\t4", op); + + if (nonlocal && flag_pic == 2) + output_asm_insn (".long\t%0", op); + if (nonlocal) + { + op[0] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_"); + SYMBOL_REF_FLAGS (op[0]) = SYMBOL_FLAG_LOCAL; + } + + targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[8])); + if (!flag_pic) + output_asm_insn (".long\t%0", op); + else + output_asm_insn (".long\t%0-%5", op); + + if (op[6]) + { + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[6])); + output_asm_insn (".long\t%2", op); + } + if (op[7]) + { + targetm.asm_out.internal_label (file, "L", + CODE_LABEL_NUMBER (op[7])); + output_asm_insn (".long\t%3", op); + } + } + final_end_function (); +} + +static bool +s390_valid_pointer_mode (enum machine_mode mode) +{ + return (mode == SImode || (TARGET_64BIT && mode == DImode)); +} + +/* Checks whether the given CALL_EXPR would use a caller + saved register. This is used to decide whether sibling call + optimization could be performed on the respective function + call. */ + +static bool +s390_call_saved_register_used (tree call_expr) +{ + CUMULATIVE_ARGS cum; + tree parameter; + enum machine_mode mode; + tree type; + rtx parm_rtx; + int reg, i; + + INIT_CUMULATIVE_ARGS (cum, NULL, NULL, 0, 0); + + for (i = 0; i < call_expr_nargs (call_expr); i++) + { + parameter = CALL_EXPR_ARG (call_expr, i); + gcc_assert (parameter); + + /* For an undeclared variable passed as parameter we will get + an ERROR_MARK node here. */ + if (TREE_CODE (parameter) == ERROR_MARK) + return true; + + type = TREE_TYPE (parameter); + gcc_assert (type); + + mode = TYPE_MODE (type); + gcc_assert (mode); + + if (pass_by_reference (&cum, mode, type, true)) + { + mode = Pmode; + type = build_pointer_type (type); + } + + parm_rtx = s390_function_arg (&cum, mode, type, 0); + + s390_function_arg_advance (&cum, mode, type, 0); + + if (!parm_rtx) + continue; + + if (REG_P (parm_rtx)) + { + for (reg = 0; + reg < HARD_REGNO_NREGS (REGNO (parm_rtx), GET_MODE (parm_rtx)); + reg++) + if (!call_used_regs[reg + REGNO (parm_rtx)]) + return true; + } + + if (GET_CODE (parm_rtx) == PARALLEL) + { + int i; + + for (i = 0; i < XVECLEN (parm_rtx, 0); i++) + { + rtx r = XEXP (XVECEXP (parm_rtx, 0, i), 0); + + gcc_assert (REG_P (r)); + + for (reg = 0; + reg < HARD_REGNO_NREGS (REGNO (r), GET_MODE (r)); + reg++) + if (!call_used_regs[reg + REGNO (r)]) + return true; + } + } + + } + return false; +} + +/* Return true if the given call expression can be + turned into a sibling call. + DECL holds the declaration of the function to be called whereas + EXP is the call expression itself. */ + +static bool +s390_function_ok_for_sibcall (tree decl, tree exp) +{ + /* The TPF epilogue uses register 1. */ + if (TARGET_TPF_PROFILING) + return false; + + /* The 31 bit PLT code uses register 12 (GOT pointer - caller saved) + which would have to be restored before the sibcall. */ + if (!TARGET_64BIT && flag_pic && decl && !targetm.binds_local_p (decl)) + return false; + + /* Register 6 on s390 is available as an argument register but unfortunately + "caller saved". This makes functions needing this register for arguments + not suitable for sibcalls. */ + return !s390_call_saved_register_used (exp); +} + +/* Return the fixed registers used for condition codes. */ + +static bool +s390_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2) +{ + *p1 = CC_REGNUM; + *p2 = INVALID_REGNUM; + + return true; +} + +/* This function is used by the call expanders of the machine description. + It emits the call insn itself together with the necessary operations + to adjust the target address and returns the emitted insn. + ADDR_LOCATION is the target address rtx + TLS_CALL the location of the thread-local symbol + RESULT_REG the register where the result of the call should be stored + RETADDR_REG the register where the return address should be stored + If this parameter is NULL_RTX the call is considered + to be a sibling call. */ + +rtx +s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg, + rtx retaddr_reg) +{ + bool plt_call = false; + rtx insn; + rtx call; + rtx clobber; + rtvec vec; + + /* Direct function calls need special treatment. */ + if (GET_CODE (addr_location) == SYMBOL_REF) + { + /* When calling a global routine in PIC mode, we must + replace the symbol itself with the PLT stub. */ + if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location)) + { + if (retaddr_reg != NULL_RTX) + { + addr_location = gen_rtx_UNSPEC (Pmode, + gen_rtvec (1, addr_location), + UNSPEC_PLT); + addr_location = gen_rtx_CONST (Pmode, addr_location); + plt_call = true; + } + else + /* For -fpic code the PLT entries might use r12 which is + call-saved. Therefore we cannot do a sibcall when + calling directly using a symbol ref. When reaching + this point we decided (in s390_function_ok_for_sibcall) + to do a sibcall for a function pointer but one of the + optimizers was able to get rid of the function pointer + by propagating the symbol ref into the call. This + optimization is illegal for S/390 so we turn the direct + call into a indirect call again. */ + addr_location = force_reg (Pmode, addr_location); + } + + /* Unless we can use the bras(l) insn, force the + routine address into a register. */ + if (!TARGET_SMALL_EXEC && !TARGET_CPU_ZARCH) + { + if (flag_pic) + addr_location = legitimize_pic_address (addr_location, 0); + else + addr_location = force_reg (Pmode, addr_location); + } + } + + /* If it is already an indirect call or the code above moved the + SYMBOL_REF to somewhere else make sure the address can be found in + register 1. */ + if (retaddr_reg == NULL_RTX + && GET_CODE (addr_location) != SYMBOL_REF + && !plt_call) + { + emit_move_insn (gen_rtx_REG (Pmode, SIBCALL_REGNUM), addr_location); + addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM); + } + + addr_location = gen_rtx_MEM (QImode, addr_location); + call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx); + + if (result_reg != NULL_RTX) + call = gen_rtx_SET (VOIDmode, result_reg, call); + + if (retaddr_reg != NULL_RTX) + { + clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg); + + if (tls_call != NULL_RTX) + vec = gen_rtvec (3, call, clobber, + gen_rtx_USE (VOIDmode, tls_call)); + else + vec = gen_rtvec (2, call, clobber); + + call = gen_rtx_PARALLEL (VOIDmode, vec); + } + + insn = emit_call_insn (call); + + /* 31-bit PLT stubs and tls calls use the GOT register implicitly. */ + if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX) + { + /* s390_function_ok_for_sibcall should + have denied sibcalls in this case. */ + gcc_assert (retaddr_reg != NULL_RTX); + + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); + } + return insn; +} + +/* Implement TARGET_CONDITIONAL_REGISTER_USAGE. */ + +static void +s390_conditional_register_usage (void) +{ + int i; + + if (flag_pic) + { + fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1; + call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1; + } + if (TARGET_CPU_ZARCH) + { + fixed_regs[BASE_REGNUM] = 0; + call_used_regs[BASE_REGNUM] = 0; + fixed_regs[RETURN_REGNUM] = 0; + call_used_regs[RETURN_REGNUM] = 0; + } + if (TARGET_64BIT) + { + for (i = 24; i < 32; i++) + call_used_regs[i] = call_really_used_regs[i] = 0; + } + else + { + for (i = 18; i < 20; i++) + call_used_regs[i] = call_really_used_regs[i] = 0; + } + + if (TARGET_SOFT_FLOAT) + { + for (i = 16; i < 32; i++) + call_used_regs[i] = fixed_regs[i] = 1; + } +} + +/* Corresponding function to eh_return expander. */ + +static GTY(()) rtx s390_tpf_eh_return_symbol; +void +s390_emit_tpf_eh_return (rtx target) +{ + rtx insn, reg; + + if (!s390_tpf_eh_return_symbol) + s390_tpf_eh_return_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tpf_eh_return"); + + reg = gen_rtx_REG (Pmode, 2); + + emit_move_insn (reg, target); + insn = s390_emit_call (s390_tpf_eh_return_symbol, NULL_RTX, reg, + gen_rtx_REG (Pmode, RETURN_REGNUM)); + use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg); + + emit_move_insn (EH_RETURN_HANDLER_RTX, reg); +} + +/* Rework the prologue/epilogue to avoid saving/restoring + registers unnecessarily. */ + +static void +s390_optimize_prologue (void) +{ + rtx insn, new_insn, next_insn; + + /* Do a final recompute of the frame-related data. */ + + s390_update_frame_layout (); + + /* If all special registers are in fact used, there's nothing we + can do, so no point in walking the insn list. */ + + if (cfun_frame_layout.first_save_gpr <= BASE_REGNUM + && cfun_frame_layout.last_save_gpr >= BASE_REGNUM + && (TARGET_CPU_ZARCH + || (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM + && cfun_frame_layout.last_save_gpr >= RETURN_REGNUM))) + return; + + /* Search for prologue/epilogue insns and replace them. */ + + for (insn = get_insns (); insn; insn = next_insn) + { + int first, last, off; + rtx set, base, offset; + + next_insn = NEXT_INSN (insn); + + if (GET_CODE (insn) != INSN) + continue; + + if (GET_CODE (PATTERN (insn)) == PARALLEL + && store_multiple_operation (PATTERN (insn), VOIDmode)) + { + set = XVECEXP (PATTERN (insn), 0, 0); + first = REGNO (SET_SRC (set)); + last = first + XVECLEN (PATTERN (insn), 0) - 1; + offset = const0_rtx; + base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset); + off = INTVAL (offset); + + if (GET_CODE (base) != REG || off < 0) + continue; + if (cfun_frame_layout.first_save_gpr != -1 + && (cfun_frame_layout.first_save_gpr < first + || cfun_frame_layout.last_save_gpr > last)) + continue; + if (REGNO (base) != STACK_POINTER_REGNUM + && REGNO (base) != HARD_FRAME_POINTER_REGNUM) + continue; + if (first > BASE_REGNUM || last < BASE_REGNUM) + continue; + + if (cfun_frame_layout.first_save_gpr != -1) + { + new_insn = save_gprs (base, + off + (cfun_frame_layout.first_save_gpr + - first) * UNITS_PER_LONG, + cfun_frame_layout.first_save_gpr, + cfun_frame_layout.last_save_gpr); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); + } + + remove_insn (insn); + continue; + } + + if (cfun_frame_layout.first_save_gpr == -1 + && GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_SRC (PATTERN (insn))) == REG + && (REGNO (SET_SRC (PATTERN (insn))) == BASE_REGNUM + || (!TARGET_CPU_ZARCH + && REGNO (SET_SRC (PATTERN (insn))) == RETURN_REGNUM)) + && GET_CODE (SET_DEST (PATTERN (insn))) == MEM) + { + set = PATTERN (insn); + first = REGNO (SET_SRC (set)); + offset = const0_rtx; + base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset); + off = INTVAL (offset); + + if (GET_CODE (base) != REG || off < 0) + continue; + if (REGNO (base) != STACK_POINTER_REGNUM + && REGNO (base) != HARD_FRAME_POINTER_REGNUM) + continue; + + remove_insn (insn); + continue; + } + + if (GET_CODE (PATTERN (insn)) == PARALLEL + && load_multiple_operation (PATTERN (insn), VOIDmode)) + { + set = XVECEXP (PATTERN (insn), 0, 0); + first = REGNO (SET_DEST (set)); + last = first + XVECLEN (PATTERN (insn), 0) - 1; + offset = const0_rtx; + base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset); + off = INTVAL (offset); + + if (GET_CODE (base) != REG || off < 0) + continue; + if (cfun_frame_layout.first_restore_gpr != -1 + && (cfun_frame_layout.first_restore_gpr < first + || cfun_frame_layout.last_restore_gpr > last)) + continue; + if (REGNO (base) != STACK_POINTER_REGNUM + && REGNO (base) != HARD_FRAME_POINTER_REGNUM) + continue; + if (first > BASE_REGNUM || last < BASE_REGNUM) + continue; + + if (cfun_frame_layout.first_restore_gpr != -1) + { + new_insn = restore_gprs (base, + off + (cfun_frame_layout.first_restore_gpr + - first) * UNITS_PER_LONG, + cfun_frame_layout.first_restore_gpr, + cfun_frame_layout.last_restore_gpr); + new_insn = emit_insn_before (new_insn, insn); + INSN_ADDRESSES_NEW (new_insn, -1); + } + + remove_insn (insn); + continue; + } + + if (cfun_frame_layout.first_restore_gpr == -1 + && GET_CODE (PATTERN (insn)) == SET + && GET_CODE (SET_DEST (PATTERN (insn))) == REG + && (REGNO (SET_DEST (PATTERN (insn))) == BASE_REGNUM + || (!TARGET_CPU_ZARCH + && REGNO (SET_DEST (PATTERN (insn))) == RETURN_REGNUM)) + && GET_CODE (SET_SRC (PATTERN (insn))) == MEM) + { + set = PATTERN (insn); + first = REGNO (SET_DEST (set)); + offset = const0_rtx; + base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset); + off = INTVAL (offset); + + if (GET_CODE (base) != REG || off < 0) + continue; + if (REGNO (base) != STACK_POINTER_REGNUM + && REGNO (base) != HARD_FRAME_POINTER_REGNUM) + continue; + + remove_insn (insn); + continue; + } + } +} + +/* On z10 and later the dynamic branch prediction must see the + backward jump within a certain windows. If not it falls back to + the static prediction. This function rearranges the loop backward + branch in a way which makes the static prediction always correct. + The function returns true if it added an instruction. */ +static bool +s390_fix_long_loop_prediction (rtx insn) +{ + rtx set = single_set (insn); + rtx code_label, label_ref, new_label; + rtx uncond_jump; + rtx cur_insn; + rtx tmp; + int distance; + + /* This will exclude branch on count and branch on index patterns + since these are correctly statically predicted. */ + if (!set + || SET_DEST (set) != pc_rtx + || GET_CODE (SET_SRC(set)) != IF_THEN_ELSE) + return false; + + label_ref = (GET_CODE (XEXP (SET_SRC (set), 1)) == LABEL_REF ? + XEXP (SET_SRC (set), 1) : XEXP (SET_SRC (set), 2)); + + gcc_assert (GET_CODE (label_ref) == LABEL_REF); + + code_label = XEXP (label_ref, 0); + + if (INSN_ADDRESSES (INSN_UID (code_label)) == -1 + || INSN_ADDRESSES (INSN_UID (insn)) == -1 + || (INSN_ADDRESSES (INSN_UID (insn)) + - INSN_ADDRESSES (INSN_UID (code_label)) < PREDICT_DISTANCE)) + return false; + + for (distance = 0, cur_insn = PREV_INSN (insn); + distance < PREDICT_DISTANCE - 6; + distance += get_attr_length (cur_insn), cur_insn = PREV_INSN (cur_insn)) + if (!cur_insn || JUMP_P (cur_insn) || LABEL_P (cur_insn)) + return false; + + new_label = gen_label_rtx (); + uncond_jump = emit_jump_insn_after ( + gen_rtx_SET (VOIDmode, pc_rtx, + gen_rtx_LABEL_REF (VOIDmode, code_label)), + insn); + emit_label_after (new_label, uncond_jump); + + tmp = XEXP (SET_SRC (set), 1); + XEXP (SET_SRC (set), 1) = XEXP (SET_SRC (set), 2); + XEXP (SET_SRC (set), 2) = tmp; + INSN_CODE (insn) = -1; + + XEXP (label_ref, 0) = new_label; + JUMP_LABEL (insn) = new_label; + JUMP_LABEL (uncond_jump) = code_label; + + return true; +} + +/* Returns 1 if INSN reads the value of REG for purposes not related + to addressing of memory, and 0 otherwise. */ +static int +s390_non_addr_reg_read_p (rtx reg, rtx insn) +{ + return reg_referenced_p (reg, PATTERN (insn)) + && !reg_used_in_mem_p (REGNO (reg), PATTERN (insn)); +} + +/* Starting from INSN find_cond_jump looks downwards in the insn + stream for a single jump insn which is the last user of the + condition code set in INSN. */ +static rtx +find_cond_jump (rtx insn) +{ + for (; insn; insn = NEXT_INSN (insn)) + { + rtx ite, cc; + + if (LABEL_P (insn)) + break; + + if (!JUMP_P (insn)) + { + if (reg_mentioned_p (gen_rtx_REG (CCmode, CC_REGNUM), insn)) + break; + continue; + } + + /* This will be triggered by a return. */ + if (GET_CODE (PATTERN (insn)) != SET) + break; + + gcc_assert (SET_DEST (PATTERN (insn)) == pc_rtx); + ite = SET_SRC (PATTERN (insn)); + + if (GET_CODE (ite) != IF_THEN_ELSE) + break; + + cc = XEXP (XEXP (ite, 0), 0); + if (!REG_P (cc) || !CC_REGNO_P (REGNO (cc))) + break; + + if (find_reg_note (insn, REG_DEAD, cc)) + return insn; + break; + } + + return NULL_RTX; +} + +/* Swap the condition in COND and the operands in OP0 and OP1 so that + the semantics does not change. If NULL_RTX is passed as COND the + function tries to find the conditional jump starting with INSN. */ +static void +s390_swap_cmp (rtx cond, rtx *op0, rtx *op1, rtx insn) +{ + rtx tmp = *op0; + + if (cond == NULL_RTX) + { + rtx jump = find_cond_jump (NEXT_INSN (insn)); + jump = jump ? single_set (jump) : NULL_RTX; + + if (jump == NULL_RTX) + return; + + cond = XEXP (XEXP (jump, 1), 0); + } + + *op0 = *op1; + *op1 = tmp; + PUT_CODE (cond, swap_condition (GET_CODE (cond))); +} + +/* On z10, instructions of the compare-and-branch family have the + property to access the register occurring as second operand with + its bits complemented. If such a compare is grouped with a second + instruction that accesses the same register non-complemented, and + if that register's value is delivered via a bypass, then the + pipeline recycles, thereby causing significant performance decline. + This function locates such situations and exchanges the two + operands of the compare. The function return true whenever it + added an insn. */ +static bool +s390_z10_optimize_cmp (rtx insn) +{ + rtx prev_insn, next_insn; + bool insn_added_p = false; + rtx cond, *op0, *op1; + + if (GET_CODE (PATTERN (insn)) == PARALLEL) + { + /* Handle compare and branch and branch on count + instructions. */ + rtx pattern = single_set (insn); + + if (!pattern + || SET_DEST (pattern) != pc_rtx + || GET_CODE (SET_SRC (pattern)) != IF_THEN_ELSE) + return false; + + cond = XEXP (SET_SRC (pattern), 0); + op0 = &XEXP (cond, 0); + op1 = &XEXP (cond, 1); + } + else if (GET_CODE (PATTERN (insn)) == SET) + { + rtx src, dest; + + /* Handle normal compare instructions. */ + src = SET_SRC (PATTERN (insn)); + dest = SET_DEST (PATTERN (insn)); + + if (!REG_P (dest) + || !CC_REGNO_P (REGNO (dest)) + || GET_CODE (src) != COMPARE) + return false; + + /* s390_swap_cmp will try to find the conditional + jump when passing NULL_RTX as condition. */ + cond = NULL_RTX; + op0 = &XEXP (src, 0); + op1 = &XEXP (src, 1); + } + else + return false; + + if (!REG_P (*op0) || !REG_P (*op1)) + return false; + + if (GET_MODE_CLASS (GET_MODE (*op0)) != MODE_INT) + return false; + + /* Swap the COMPARE arguments and its mask if there is a + conflicting access in the previous insn. */ + prev_insn = prev_active_insn (insn); + if (prev_insn != NULL_RTX && INSN_P (prev_insn) + && reg_referenced_p (*op1, PATTERN (prev_insn))) + s390_swap_cmp (cond, op0, op1, insn); + + /* Check if there is a conflict with the next insn. If there + was no conflict with the previous insn, then swap the + COMPARE arguments and its mask. If we already swapped + the operands, or if swapping them would cause a conflict + with the previous insn, issue a NOP after the COMPARE in + order to separate the two instuctions. */ + next_insn = next_active_insn (insn); + if (next_insn != NULL_RTX && INSN_P (next_insn) + && s390_non_addr_reg_read_p (*op1, next_insn)) + { + if (prev_insn != NULL_RTX && INSN_P (prev_insn) + && s390_non_addr_reg_read_p (*op0, prev_insn)) + { + if (REGNO (*op1) == 0) + emit_insn_after (gen_nop1 (), insn); + else + emit_insn_after (gen_nop (), insn); + insn_added_p = true; + } + else + s390_swap_cmp (cond, op0, op1, insn); + } + return insn_added_p; +} + +/* Perform machine-dependent processing. */ + +static void +s390_reorg (void) +{ + bool pool_overflow = false; + + /* Make sure all splits have been performed; splits after + machine_dependent_reorg might confuse insn length counts. */ + split_all_insns_noflow (); + + /* Install the main literal pool and the associated base + register load insns. + + In addition, there are two problematic situations we need + to correct: + + - the literal pool might be > 4096 bytes in size, so that + some of its elements cannot be directly accessed + + - a branch target might be > 64K away from the branch, so that + it is not possible to use a PC-relative instruction. + + To fix those, we split the single literal pool into multiple + pool chunks, reloading the pool base register at various + points throughout the function to ensure it always points to + the pool chunk the following code expects, and / or replace + PC-relative branches by absolute branches. + + However, the two problems are interdependent: splitting the + literal pool can move a branch further away from its target, + causing the 64K limit to overflow, and on the other hand, + replacing a PC-relative branch by an absolute branch means + we need to put the branch target address into the literal + pool, possibly causing it to overflow. + + So, we loop trying to fix up both problems until we manage + to satisfy both conditions at the same time. Note that the + loop is guaranteed to terminate as every pass of the loop + strictly decreases the total number of PC-relative branches + in the function. (This is not completely true as there + might be branch-over-pool insns introduced by chunkify_start. + Those never need to be split however.) */ + + for (;;) + { + struct constant_pool *pool = NULL; + + /* Collect the literal pool. */ + if (!pool_overflow) + { + pool = s390_mainpool_start (); + if (!pool) + pool_overflow = true; + } + + /* If literal pool overflowed, start to chunkify it. */ + if (pool_overflow) + pool = s390_chunkify_start (); + + /* Split out-of-range branches. If this has created new + literal pool entries, cancel current chunk list and + recompute it. zSeries machines have large branch + instructions, so we never need to split a branch. */ + if (!TARGET_CPU_ZARCH && s390_split_branches ()) + { + if (pool_overflow) + s390_chunkify_cancel (pool); + else + s390_mainpool_cancel (pool); + + continue; + } + + /* If we made it up to here, both conditions are satisfied. + Finish up literal pool related changes. */ + if (pool_overflow) + s390_chunkify_finish (pool); + else + s390_mainpool_finish (pool); + + /* We're done splitting branches. */ + cfun->machine->split_branches_pending_p = false; + break; + } + + /* Generate out-of-pool execute target insns. */ + if (TARGET_CPU_ZARCH) + { + rtx insn, label, target; + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + label = s390_execute_label (insn); + if (!label) + continue; + + gcc_assert (label != const0_rtx); + + target = emit_label (XEXP (label, 0)); + INSN_ADDRESSES_NEW (target, -1); + + target = emit_insn (s390_execute_target (insn)); + INSN_ADDRESSES_NEW (target, -1); + } + } + + /* Try to optimize prologue and epilogue further. */ + s390_optimize_prologue (); + + /* Walk over the insns and do some >=z10 specific changes. */ + if (s390_tune == PROCESSOR_2097_Z10 + || s390_tune == PROCESSOR_2817_Z196) + { + rtx insn; + bool insn_added_p = false; + + /* The insn lengths and addresses have to be up to date for the + following manipulations. */ + shorten_branches (get_insns ()); + + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + { + if (!INSN_P (insn) || INSN_CODE (insn) <= 0) + continue; + + if (JUMP_P (insn)) + insn_added_p |= s390_fix_long_loop_prediction (insn); + + if ((GET_CODE (PATTERN (insn)) == PARALLEL + || GET_CODE (PATTERN (insn)) == SET) + && s390_tune == PROCESSOR_2097_Z10) + insn_added_p |= s390_z10_optimize_cmp (insn); + } + + /* Adjust branches if we added new instructions. */ + if (insn_added_p) + shorten_branches (get_insns ()); + } +} + +/* Return true if INSN is a fp load insn writing register REGNO. */ +static inline bool +s390_fpload_toreg (rtx insn, unsigned int regno) +{ + rtx set; + enum attr_type flag = s390_safe_attr_type (insn); + + if (flag != TYPE_FLOADSF && flag != TYPE_FLOADDF) + return false; + + set = single_set (insn); + + if (set == NULL_RTX) + return false; + + if (!REG_P (SET_DEST (set)) || !MEM_P (SET_SRC (set))) + return false; + + if (REGNO (SET_DEST (set)) != regno) + return false; + + return true; +} + +/* This value describes the distance to be avoided between an + aritmetic fp instruction and an fp load writing the same register. + Z10_EARLYLOAD_DISTANCE - 1 as well as Z10_EARLYLOAD_DISTANCE + 1 is + fine but the exact value has to be avoided. Otherwise the FP + pipeline will throw an exception causing a major penalty. */ +#define Z10_EARLYLOAD_DISTANCE 7 + +/* Rearrange the ready list in order to avoid the situation described + for Z10_EARLYLOAD_DISTANCE. A problematic load instruction is + moved to the very end of the ready list. */ +static void +s390_z10_prevent_earlyload_conflicts (rtx *ready, int *nready_p) +{ + unsigned int regno; + int nready = *nready_p; + rtx tmp; + int i; + rtx insn; + rtx set; + enum attr_type flag; + int distance; + + /* Skip DISTANCE - 1 active insns. */ + for (insn = last_scheduled_insn, distance = Z10_EARLYLOAD_DISTANCE - 1; + distance > 0 && insn != NULL_RTX; + distance--, insn = prev_active_insn (insn)) + if (CALL_P (insn) || JUMP_P (insn)) + return; + + if (insn == NULL_RTX) + return; + + set = single_set (insn); + + if (set == NULL_RTX || !REG_P (SET_DEST (set)) + || GET_MODE_CLASS (GET_MODE (SET_DEST (set))) != MODE_FLOAT) + return; + + flag = s390_safe_attr_type (insn); + + if (flag == TYPE_FLOADSF || flag == TYPE_FLOADDF) + return; + + regno = REGNO (SET_DEST (set)); + i = nready - 1; + + while (!s390_fpload_toreg (ready[i], regno) && i > 0) + i--; + + if (!i) + return; + + tmp = ready[i]; + memmove (&ready[1], &ready[0], sizeof (rtx) * i); + ready[0] = tmp; +} + +/* This function is called via hook TARGET_SCHED_REORDER before + issueing one insn from list READY which contains *NREADYP entries. + For target z10 it reorders load instructions to avoid early load + conflicts in the floating point pipeline */ +static int +s390_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED, + rtx *ready, int *nreadyp, int clock ATTRIBUTE_UNUSED) +{ + if (s390_tune == PROCESSOR_2097_Z10) + if (reload_completed && *nreadyp > 1) + s390_z10_prevent_earlyload_conflicts (ready, nreadyp); + + return s390_issue_rate (); +} + +/* This function is called via hook TARGET_SCHED_VARIABLE_ISSUE after + the scheduler has issued INSN. It stores the last issued insn into + last_scheduled_insn in order to make it available for + s390_sched_reorder. */ +static int +s390_sched_variable_issue (FILE *file ATTRIBUTE_UNUSED, + int verbose ATTRIBUTE_UNUSED, + rtx insn, int more) +{ + last_scheduled_insn = insn; + + if (GET_CODE (PATTERN (insn)) != USE + && GET_CODE (PATTERN (insn)) != CLOBBER) + return more - 1; + else + return more; +} + +static void +s390_sched_init (FILE *file ATTRIBUTE_UNUSED, + int verbose ATTRIBUTE_UNUSED, + int max_ready ATTRIBUTE_UNUSED) +{ + last_scheduled_insn = NULL_RTX; +} + +/* This function checks the whole of insn X for memory references. The + function always returns zero because the framework it is called + from would stop recursively analyzing the insn upon a return value + other than zero. The real result of this function is updating + counter variable MEM_COUNT. */ +static int +check_dpu (rtx *x, unsigned *mem_count) +{ + if (*x != NULL_RTX && MEM_P (*x)) + (*mem_count)++; + return 0; +} + +/* This target hook implementation for TARGET_LOOP_UNROLL_ADJUST calculates + a new number struct loop *loop should be unrolled if tuned for cpus with + a built-in stride prefetcher. + The loop is analyzed for memory accesses by calling check_dpu for + each rtx of the loop. Depending on the loop_depth and the amount of + memory accesses a new number <=nunroll is returned to improve the + behaviour of the hardware prefetch unit. */ +static unsigned +s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop) +{ + basic_block *bbs; + rtx insn; + unsigned i; + unsigned mem_count = 0; + + if (s390_tune != PROCESSOR_2097_Z10 && s390_tune != PROCESSOR_2817_Z196) + return nunroll; + + /* Count the number of memory references within the loop body. */ + bbs = get_loop_body (loop); + for (i = 0; i < loop->num_nodes; i++) + { + for (insn = BB_HEAD (bbs[i]); insn != BB_END (bbs[i]); insn = NEXT_INSN (insn)) + if (INSN_P (insn) && INSN_CODE (insn) != -1) + for_each_rtx (&insn, (rtx_function) check_dpu, &mem_count); + } + free (bbs); + + /* Prevent division by zero, and we do not need to adjust nunroll in this case. */ + if (mem_count == 0) + return nunroll; + + switch (loop_depth(loop)) + { + case 1: + return MIN (nunroll, 28 / mem_count); + case 2: + return MIN (nunroll, 22 / mem_count); + default: + return MIN (nunroll, 16 / mem_count); + } +} + +/* Initialize GCC target structure. */ + +#undef TARGET_ASM_ALIGNED_HI_OP +#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t" +#undef TARGET_ASM_ALIGNED_DI_OP +#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t" +#undef TARGET_ASM_INTEGER +#define TARGET_ASM_INTEGER s390_assemble_integer + +#undef TARGET_ASM_OPEN_PAREN +#define TARGET_ASM_OPEN_PAREN "" + +#undef TARGET_ASM_CLOSE_PAREN +#define TARGET_ASM_CLOSE_PAREN "" + +#undef TARGET_DEFAULT_TARGET_FLAGS +#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT) + +#undef TARGET_HANDLE_OPTION +#define TARGET_HANDLE_OPTION s390_handle_option + +#undef TARGET_OPTION_OVERRIDE +#define TARGET_OPTION_OVERRIDE s390_option_override + +#undef TARGET_OPTION_OPTIMIZATION_TABLE +#define TARGET_OPTION_OPTIMIZATION_TABLE s390_option_optimization_table + +#undef TARGET_OPTION_INIT_STRUCT +#define TARGET_OPTION_INIT_STRUCT s390_option_init_struct + +#undef TARGET_ENCODE_SECTION_INFO +#define TARGET_ENCODE_SECTION_INFO s390_encode_section_info + +#undef TARGET_SCALAR_MODE_SUPPORTED_P +#define TARGET_SCALAR_MODE_SUPPORTED_P s390_scalar_mode_supported_p + +#ifdef HAVE_AS_TLS +#undef TARGET_HAVE_TLS +#define TARGET_HAVE_TLS true +#endif +#undef TARGET_CANNOT_FORCE_CONST_MEM +#define TARGET_CANNOT_FORCE_CONST_MEM s390_cannot_force_const_mem + +#undef TARGET_DELEGITIMIZE_ADDRESS +#define TARGET_DELEGITIMIZE_ADDRESS s390_delegitimize_address + +#undef TARGET_LEGITIMIZE_ADDRESS +#define TARGET_LEGITIMIZE_ADDRESS s390_legitimize_address + +#undef TARGET_RETURN_IN_MEMORY +#define TARGET_RETURN_IN_MEMORY s390_return_in_memory + +#undef TARGET_INIT_BUILTINS +#define TARGET_INIT_BUILTINS s390_init_builtins +#undef TARGET_EXPAND_BUILTIN +#define TARGET_EXPAND_BUILTIN s390_expand_builtin + +#undef TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA +#define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA s390_output_addr_const_extra + +#undef TARGET_ASM_OUTPUT_MI_THUNK +#define TARGET_ASM_OUTPUT_MI_THUNK s390_output_mi_thunk +#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true + +#undef TARGET_SCHED_ADJUST_PRIORITY +#define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority +#undef TARGET_SCHED_ISSUE_RATE +#define TARGET_SCHED_ISSUE_RATE s390_issue_rate +#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD +#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD s390_first_cycle_multipass_dfa_lookahead + +#undef TARGET_SCHED_VARIABLE_ISSUE +#define TARGET_SCHED_VARIABLE_ISSUE s390_sched_variable_issue +#undef TARGET_SCHED_REORDER +#define TARGET_SCHED_REORDER s390_sched_reorder +#undef TARGET_SCHED_INIT +#define TARGET_SCHED_INIT s390_sched_init + +#undef TARGET_CANNOT_COPY_INSN_P +#define TARGET_CANNOT_COPY_INSN_P s390_cannot_copy_insn_p +#undef TARGET_RTX_COSTS +#define TARGET_RTX_COSTS s390_rtx_costs +#undef TARGET_ADDRESS_COST +#define TARGET_ADDRESS_COST s390_address_cost +#undef TARGET_REGISTER_MOVE_COST +#define TARGET_REGISTER_MOVE_COST s390_register_move_cost +#undef TARGET_MEMORY_MOVE_COST +#define TARGET_MEMORY_MOVE_COST s390_memory_move_cost + +#undef TARGET_MACHINE_DEPENDENT_REORG +#define TARGET_MACHINE_DEPENDENT_REORG s390_reorg + +#undef TARGET_VALID_POINTER_MODE +#define TARGET_VALID_POINTER_MODE s390_valid_pointer_mode + +#undef TARGET_BUILD_BUILTIN_VA_LIST +#define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list +#undef TARGET_EXPAND_BUILTIN_VA_START +#define TARGET_EXPAND_BUILTIN_VA_START s390_va_start +#undef TARGET_GIMPLIFY_VA_ARG_EXPR +#define TARGET_GIMPLIFY_VA_ARG_EXPR s390_gimplify_va_arg + +#undef TARGET_PROMOTE_FUNCTION_MODE +#define TARGET_PROMOTE_FUNCTION_MODE s390_promote_function_mode +#undef TARGET_PASS_BY_REFERENCE +#define TARGET_PASS_BY_REFERENCE s390_pass_by_reference + +#undef TARGET_FUNCTION_OK_FOR_SIBCALL +#define TARGET_FUNCTION_OK_FOR_SIBCALL s390_function_ok_for_sibcall +#undef TARGET_FUNCTION_ARG +#define TARGET_FUNCTION_ARG s390_function_arg +#undef TARGET_FUNCTION_ARG_ADVANCE +#define TARGET_FUNCTION_ARG_ADVANCE s390_function_arg_advance +#undef TARGET_FUNCTION_VALUE +#define TARGET_FUNCTION_VALUE s390_function_value +#undef TARGET_LIBCALL_VALUE +#define TARGET_LIBCALL_VALUE s390_libcall_value + +#undef TARGET_FIXED_CONDITION_CODE_REGS +#define TARGET_FIXED_CONDITION_CODE_REGS s390_fixed_condition_code_regs + +#undef TARGET_CC_MODES_COMPATIBLE +#define TARGET_CC_MODES_COMPATIBLE s390_cc_modes_compatible + +#undef TARGET_INVALID_WITHIN_DOLOOP +#define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_const_rtx_null + +#ifdef HAVE_AS_TLS +#undef TARGET_ASM_OUTPUT_DWARF_DTPREL +#define TARGET_ASM_OUTPUT_DWARF_DTPREL s390_output_dwarf_dtprel +#endif + +#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING +#undef TARGET_MANGLE_TYPE +#define TARGET_MANGLE_TYPE s390_mangle_type +#endif + +#undef TARGET_SCALAR_MODE_SUPPORTED_P +#define TARGET_SCALAR_MODE_SUPPORTED_P s390_scalar_mode_supported_p + +#undef TARGET_PREFERRED_RELOAD_CLASS +#define TARGET_PREFERRED_RELOAD_CLASS s390_preferred_reload_class + +#undef TARGET_SECONDARY_RELOAD +#define TARGET_SECONDARY_RELOAD s390_secondary_reload + +#undef TARGET_LIBGCC_CMP_RETURN_MODE +#define TARGET_LIBGCC_CMP_RETURN_MODE s390_libgcc_cmp_return_mode + +#undef TARGET_LIBGCC_SHIFT_COUNT_MODE +#define TARGET_LIBGCC_SHIFT_COUNT_MODE s390_libgcc_shift_count_mode + +#undef TARGET_LEGITIMATE_ADDRESS_P +#define TARGET_LEGITIMATE_ADDRESS_P s390_legitimate_address_p + +#undef TARGET_CAN_ELIMINATE +#define TARGET_CAN_ELIMINATE s390_can_eliminate + +#undef TARGET_CONDITIONAL_REGISTER_USAGE +#define TARGET_CONDITIONAL_REGISTER_USAGE s390_conditional_register_usage + +#undef TARGET_LOOP_UNROLL_ADJUST +#define TARGET_LOOP_UNROLL_ADJUST s390_loop_unroll_adjust + +#undef TARGET_ASM_TRAMPOLINE_TEMPLATE +#define TARGET_ASM_TRAMPOLINE_TEMPLATE s390_asm_trampoline_template +#undef TARGET_TRAMPOLINE_INIT +#define TARGET_TRAMPOLINE_INIT s390_trampoline_init + +#undef TARGET_UNWIND_WORD_MODE +#define TARGET_UNWIND_WORD_MODE s390_unwind_word_mode + +struct gcc_target targetm = TARGET_INITIALIZER; + +#include "gt-s390.h" diff --git a/gcc/config/s390/s390.h b/gcc/config/s390/s390.h new file mode 100644 index 000000000..ec395e295 --- /dev/null +++ b/gcc/config/s390/s390.h @@ -0,0 +1,954 @@ +/* Definitions of target machine for GNU compiler, for IBM S/390 + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, + 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. + Contributed by Hartmut Penner (hpenner@de.ibm.com) and + Ulrich Weigand (uweigand@de.ibm.com). + Andreas Krebbel (Andreas.Krebbel@de.ibm.com) + +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/>. */ + +#ifndef _S390_H +#define _S390_H + +/* Which processor to generate code or schedule for. The cpu attribute + defines a list that mirrors this list, so changes to s390.md must be + made at the same time. */ + +enum processor_type +{ + PROCESSOR_9672_G5, + PROCESSOR_9672_G6, + PROCESSOR_2064_Z900, + PROCESSOR_2084_Z990, + PROCESSOR_2094_Z9_109, + PROCESSOR_2097_Z10, + PROCESSOR_2817_Z196, + PROCESSOR_max +}; + +/* Optional architectural facilities supported by the processor. */ + +enum processor_flags +{ + PF_IEEE_FLOAT = 1, + PF_ZARCH = 2, + PF_LONG_DISPLACEMENT = 4, + PF_EXTIMM = 8, + PF_DFP = 16, + PF_Z10 = 32, + PF_Z196 = 64 +}; + +extern enum processor_type s390_tune; +extern int s390_tune_flags; + +/* This is necessary to avoid a warning about comparing different enum + types. */ +#define s390_tune_attr ((enum attr_cpu)s390_tune) + +extern enum processor_type s390_arch; +extern int s390_arch_flags; + +/* These flags indicate that the generated code should run on a cpu + providing the respective hardware facility regardless of the + current cpu mode (ESA or z/Architecture). */ + +#define TARGET_CPU_IEEE_FLOAT \ + (s390_arch_flags & PF_IEEE_FLOAT) +#define TARGET_CPU_ZARCH \ + (s390_arch_flags & PF_ZARCH) +#define TARGET_CPU_LONG_DISPLACEMENT \ + (s390_arch_flags & PF_LONG_DISPLACEMENT) +#define TARGET_CPU_EXTIMM \ + (s390_arch_flags & PF_EXTIMM) +#define TARGET_CPU_DFP \ + (s390_arch_flags & PF_DFP) +#define TARGET_CPU_Z10 \ + (s390_arch_flags & PF_Z10) +#define TARGET_CPU_Z196 \ + (s390_arch_flags & PF_Z196) + +/* These flags indicate that the generated code should run on a cpu + providing the respective hardware facility when run in + z/Architecture mode. */ + +#define TARGET_LONG_DISPLACEMENT \ + (TARGET_ZARCH && TARGET_CPU_LONG_DISPLACEMENT) +#define TARGET_EXTIMM \ + (TARGET_ZARCH && TARGET_CPU_EXTIMM) +#define TARGET_DFP \ + (TARGET_ZARCH && TARGET_CPU_DFP && TARGET_HARD_FLOAT) +#define TARGET_Z10 \ + (TARGET_ZARCH && TARGET_CPU_Z10) +#define TARGET_Z196 \ + (TARGET_ZARCH && TARGET_CPU_Z196) + + +#define TARGET_AVOID_CMP_AND_BRANCH (s390_tune == PROCESSOR_2817_Z196) + +/* Run-time target specification. */ + +/* Defaults for option flags defined only on some subtargets. */ +#ifndef TARGET_TPF_PROFILING +#define TARGET_TPF_PROFILING 0 +#endif + +/* This will be overridden by OS headers. */ +#define TARGET_TPF 0 + +/* Target CPU builtins. */ +#define TARGET_CPU_CPP_BUILTINS() \ + do \ + { \ + builtin_assert ("cpu=s390"); \ + builtin_assert ("machine=s390"); \ + builtin_define ("__s390__"); \ + if (TARGET_ZARCH) \ + builtin_define ("__zarch__"); \ + if (TARGET_64BIT) \ + builtin_define ("__s390x__"); \ + if (TARGET_LONG_DOUBLE_128) \ + builtin_define ("__LONG_DOUBLE_128__"); \ + } \ + while (0) + +#ifdef DEFAULT_TARGET_64BIT +#define TARGET_DEFAULT (MASK_64BIT | MASK_ZARCH | MASK_HARD_DFP) +#else +#define TARGET_DEFAULT 0 +#endif + +/* Support for configure-time defaults. */ +#define OPTION_DEFAULT_SPECS \ + { "mode", "%{!mesa:%{!mzarch:-m%(VALUE)}}" }, \ + { "arch", "%{!march=*:-march=%(VALUE)}" }, \ + { "tune", "%{!mtune=*:-mtune=%(VALUE)}" } + +/* Defaulting rules. */ +#ifdef DEFAULT_TARGET_64BIT +#define DRIVER_SELF_SPECS \ + "%{!m31:%{!m64:-m64}}", \ + "%{!mesa:%{!mzarch:%{m31:-mesa}%{m64:-mzarch}}}", \ + "%{!march=*:%{mesa:-march=g5}%{mzarch:-march=z900}}" +#else +#define DRIVER_SELF_SPECS \ + "%{!m31:%{!m64:-m31}}", \ + "%{!mesa:%{!mzarch:%{m31:-mesa}%{m64:-mzarch}}}", \ + "%{!march=*:%{mesa:-march=g5}%{mzarch:-march=z900}}" +#endif + +/* Target version string. Overridden by the OS header. */ +#ifdef DEFAULT_TARGET_64BIT +#define TARGET_VERSION fprintf (stderr, " (zSeries)"); +#else +#define TARGET_VERSION fprintf (stderr, " (S/390)"); +#endif + +/* Constants needed to control the TEST DATA CLASS (TDC) instruction. */ +#define S390_TDC_POSITIVE_ZERO (1 << 11) +#define S390_TDC_NEGATIVE_ZERO (1 << 10) +#define S390_TDC_POSITIVE_NORMALIZED_BFP_NUMBER (1 << 9) +#define S390_TDC_NEGATIVE_NORMALIZED_BFP_NUMBER (1 << 8) +#define S390_TDC_POSITIVE_DENORMALIZED_BFP_NUMBER (1 << 7) +#define S390_TDC_NEGATIVE_DENORMALIZED_BFP_NUMBER (1 << 6) +#define S390_TDC_POSITIVE_INFINITY (1 << 5) +#define S390_TDC_NEGATIVE_INFINITY (1 << 4) +#define S390_TDC_POSITIVE_QUIET_NAN (1 << 3) +#define S390_TDC_NEGATIVE_QUIET_NAN (1 << 2) +#define S390_TDC_POSITIVE_SIGNALING_NAN (1 << 1) +#define S390_TDC_NEGATIVE_SIGNALING_NAN (1 << 0) + +/* The following values are different for DFP. */ +#define S390_TDC_POSITIVE_DENORMALIZED_DFP_NUMBER (1 << 9) +#define S390_TDC_NEGATIVE_DENORMALIZED_DFP_NUMBER (1 << 8) +#define S390_TDC_POSITIVE_NORMALIZED_DFP_NUMBER (1 << 7) +#define S390_TDC_NEGATIVE_NORMALIZED_DFP_NUMBER (1 << 6) + +/* For signbit, the BFP-DFP-difference makes no difference. */ +#define S390_TDC_SIGNBIT_SET (S390_TDC_NEGATIVE_ZERO \ + | S390_TDC_NEGATIVE_NORMALIZED_BFP_NUMBER \ + | S390_TDC_NEGATIVE_DENORMALIZED_BFP_NUMBER\ + | S390_TDC_NEGATIVE_INFINITY \ + | S390_TDC_NEGATIVE_QUIET_NAN \ + | S390_TDC_NEGATIVE_SIGNALING_NAN ) + +#define S390_TDC_INFINITY (S390_TDC_POSITIVE_INFINITY \ + | S390_TDC_NEGATIVE_INFINITY ) + +/* Target machine storage layout. */ + +/* Everything is big-endian. */ +#define BITS_BIG_ENDIAN 1 +#define BYTES_BIG_ENDIAN 1 +#define WORDS_BIG_ENDIAN 1 + +#define STACK_SIZE_MODE (Pmode) + +#ifndef IN_LIBGCC2 + +/* Width of a word, in units (bytes). */ + #define UNITS_PER_WORD (TARGET_ZARCH ? 8 : 4) + +/* Width of a pointer. To be used instead of UNITS_PER_WORD in + ABI-relevant contexts. This always matches + GET_MODE_SIZE (Pmode). */ + #define UNITS_PER_LONG (TARGET_64BIT ? 8 : 4) + #define MIN_UNITS_PER_WORD 4 + #define MAX_BITS_PER_WORD 64 +#else + + /* In libgcc, UNITS_PER_WORD has ABI-relevant effects, e.g. whether + the library should export TImode functions or not. Thus, we have + to redefine UNITS_PER_WORD depending on __s390x__ for libgcc. */ + #ifdef __s390x__ + #define UNITS_PER_WORD 8 + #else + #define UNITS_PER_WORD 4 + #endif +#endif + +/* Width of a pointer, in bits. */ +#define POINTER_SIZE (TARGET_64BIT ? 64 : 32) + +/* Allocation boundary (in *bits*) for storing arguments in argument list. */ +#define PARM_BOUNDARY (TARGET_64BIT ? 64 : 32) + +/* Boundary (in *bits*) on which stack pointer should be aligned. */ +#define STACK_BOUNDARY 64 + +/* Allocation boundary (in *bits*) for the code of a function. */ +#define FUNCTION_BOUNDARY 32 + +/* There is no point aligning anything to a rounder boundary than this. */ +#define BIGGEST_ALIGNMENT 64 + +/* Alignment of field after `int : 0' in a structure. */ +#define EMPTY_FIELD_BOUNDARY 32 + +/* Alignment on even addresses for LARL instruction. */ +#define CONSTANT_ALIGNMENT(EXP, ALIGN) (ALIGN) < 16 ? 16 : (ALIGN) +#define DATA_ALIGNMENT(TYPE, ALIGN) (ALIGN) < 16 ? 16 : (ALIGN) + +/* Alignment is not required by the hardware. */ +#define STRICT_ALIGNMENT 0 + +/* Mode of stack savearea. + FUNCTION is VOIDmode because calling convention maintains SP. + BLOCK needs Pmode for SP. + NONLOCAL needs twice Pmode to maintain both backchain and SP. */ +#define STACK_SAVEAREA_MODE(LEVEL) \ + (LEVEL == SAVE_FUNCTION ? VOIDmode \ + : LEVEL == SAVE_NONLOCAL ? (TARGET_64BIT ? OImode : TImode) : Pmode) + + +/* Type layout. */ + +/* Sizes in bits of the source language data types. */ +#define SHORT_TYPE_SIZE 16 +#define INT_TYPE_SIZE 32 +#define LONG_TYPE_SIZE (TARGET_64BIT ? 64 : 32) +#define LONG_LONG_TYPE_SIZE 64 +#define FLOAT_TYPE_SIZE 32 +#define DOUBLE_TYPE_SIZE 64 +#define LONG_DOUBLE_TYPE_SIZE (TARGET_LONG_DOUBLE_128 ? 128 : 64) + +/* Define this to set long double type size to use in libgcc2.c, which can + not depend on target_flags. */ +#ifdef __LONG_DOUBLE_128__ +#define LIBGCC2_LONG_DOUBLE_TYPE_SIZE 128 +#else +#define LIBGCC2_LONG_DOUBLE_TYPE_SIZE 64 +#endif + +/* Work around target_flags dependency in ada/targtyps.c. */ +#define WIDEST_HARDWARE_FP_SIZE 64 + +/* We use "unsigned char" as default. */ +#define DEFAULT_SIGNED_CHAR 0 + + +/* Register usage. */ + +/* We have 16 general purpose registers (registers 0-15), + and 16 floating point registers (registers 16-31). + (On non-IEEE machines, we have only 4 fp registers.) + + Amongst the general purpose registers, some are used + for specific purposes: + GPR 11: Hard frame pointer (if needed) + GPR 12: Global offset table pointer (if needed) + GPR 13: Literal pool base register + GPR 14: Return address register + GPR 15: Stack pointer + + Registers 32-35 are 'fake' hard registers that do not + correspond to actual hardware: + Reg 32: Argument pointer + Reg 33: Condition code + Reg 34: Frame pointer + Reg 35: Return address pointer + + Registers 36 and 37 are mapped to access registers + 0 and 1, used to implement thread-local storage. */ + +#define FIRST_PSEUDO_REGISTER 38 + +/* Standard register usage. */ +#define GENERAL_REGNO_P(N) ((int)(N) >= 0 && (N) < 16) +#define ADDR_REGNO_P(N) ((N) >= 1 && (N) < 16) +#define FP_REGNO_P(N) ((N) >= 16 && (N) < 32) +#define CC_REGNO_P(N) ((N) == 33) +#define FRAME_REGNO_P(N) ((N) == 32 || (N) == 34 || (N) == 35) +#define ACCESS_REGNO_P(N) ((N) == 36 || (N) == 37) + +#define GENERAL_REG_P(X) (REG_P (X) && GENERAL_REGNO_P (REGNO (X))) +#define ADDR_REG_P(X) (REG_P (X) && ADDR_REGNO_P (REGNO (X))) +#define FP_REG_P(X) (REG_P (X) && FP_REGNO_P (REGNO (X))) +#define CC_REG_P(X) (REG_P (X) && CC_REGNO_P (REGNO (X))) +#define FRAME_REG_P(X) (REG_P (X) && FRAME_REGNO_P (REGNO (X))) +#define ACCESS_REG_P(X) (REG_P (X) && ACCESS_REGNO_P (REGNO (X))) + +/* Set up fixed registers and calling convention: + + GPRs 0-5 are always call-clobbered, + GPRs 6-15 are always call-saved. + GPR 12 is fixed if used as GOT pointer. + GPR 13 is always fixed (as literal pool pointer). + GPR 14 is always fixed on S/390 machines (as return address). + GPR 15 is always fixed (as stack pointer). + The 'fake' hard registers are call-clobbered and fixed. + The access registers are call-saved and fixed. + + On 31-bit, FPRs 18-19 are call-clobbered; + on 64-bit, FPRs 24-31 are call-clobbered. + The remaining FPRs are call-saved. */ + +#define FIXED_REGISTERS \ +{ 0, 0, 0, 0, \ + 0, 0, 0, 0, \ + 0, 0, 0, 0, \ + 0, 1, 1, 1, \ + 0, 0, 0, 0, \ + 0, 0, 0, 0, \ + 0, 0, 0, 0, \ + 0, 0, 0, 0, \ + 1, 1, 1, 1, \ + 1, 1 } + +#define CALL_USED_REGISTERS \ +{ 1, 1, 1, 1, \ + 1, 1, 0, 0, \ + 0, 0, 0, 0, \ + 0, 1, 1, 1, \ + 1, 1, 1, 1, \ + 1, 1, 1, 1, \ + 1, 1, 1, 1, \ + 1, 1, 1, 1, \ + 1, 1, 1, 1, \ + 1, 1 } + +#define CALL_REALLY_USED_REGISTERS \ +{ 1, 1, 1, 1, \ + 1, 1, 0, 0, \ + 0, 0, 0, 0, \ + 0, 0, 0, 0, \ + 1, 1, 1, 1, \ + 1, 1, 1, 1, \ + 1, 1, 1, 1, \ + 1, 1, 1, 1, \ + 1, 1, 1, 1, \ + 0, 0 } + +/* Preferred register allocation order. */ +#define REG_ALLOC_ORDER \ +{ 1, 2, 3, 4, 5, 0, 12, 11, 10, 9, 8, 7, 6, 14, 13, \ + 16, 17, 18, 19, 20, 21, 22, 23, \ + 24, 25, 26, 27, 28, 29, 30, 31, \ + 15, 32, 33, 34, 35, 36, 37 } + + +/* Fitting values into registers. */ + +/* Integer modes <= word size fit into any GPR. + Integer modes > word size fit into successive GPRs, starting with + an even-numbered register. + SImode and DImode fit into FPRs as well. + + Floating point modes <= word size fit into any FPR or GPR. + Floating point modes > word size (i.e. DFmode on 32-bit) fit + into any FPR, or an even-odd GPR pair. + TFmode fits only into an even-odd FPR pair. + + Complex floating point modes fit either into two FPRs, or into + successive GPRs (again starting with an even number). + TCmode fits only into two successive even-odd FPR pairs. + + Condition code modes fit only into the CC register. */ + +/* Because all registers in a class have the same size HARD_REGNO_NREGS + is equivalent to CLASS_MAX_NREGS. */ +#define HARD_REGNO_NREGS(REGNO, MODE) \ + s390_class_max_nregs (REGNO_REG_CLASS (REGNO), (MODE)) + +#define HARD_REGNO_MODE_OK(REGNO, MODE) \ + s390_hard_regno_mode_ok ((REGNO), (MODE)) + +#define HARD_REGNO_RENAME_OK(FROM, TO) \ + s390_hard_regno_rename_ok (FROM, TO) + +#define MODES_TIEABLE_P(MODE1, MODE2) \ + (((MODE1) == SFmode || (MODE1) == DFmode) \ + == ((MODE2) == SFmode || (MODE2) == DFmode)) + +/* When generating code that runs in z/Architecture mode, + but conforms to the 31-bit ABI, GPRs can hold 8 bytes; + the ABI guarantees only that the lower 4 bytes are + saved across calls, however. */ +#define HARD_REGNO_CALL_PART_CLOBBERED(REGNO, MODE) \ + (!TARGET_64BIT && TARGET_ZARCH \ + && GET_MODE_SIZE (MODE) > 4 \ + && (((REGNO) >= 6 && (REGNO) <= 15) || (REGNO) == 32)) + +/* Maximum number of registers to represent a value of mode MODE + in a register of class CLASS. */ +#define CLASS_MAX_NREGS(CLASS, MODE) \ + s390_class_max_nregs ((CLASS), (MODE)) + +/* If a 4-byte value is loaded into a FPR, it is placed into the + *upper* half of the register, not the lower. Therefore, we + cannot use SUBREGs to switch between modes in FP registers. + Likewise for access registers, since they have only half the + word size on 64-bit. */ +#define CANNOT_CHANGE_MODE_CLASS(FROM, TO, CLASS) \ + (GET_MODE_SIZE (FROM) != GET_MODE_SIZE (TO) \ + ? ((reg_classes_intersect_p (FP_REGS, CLASS) \ + && (GET_MODE_SIZE (FROM) < 8 || GET_MODE_SIZE (TO) < 8)) \ + || reg_classes_intersect_p (ACCESS_REGS, CLASS)) : 0) + +/* Register classes. */ + +/* We use the following register classes: + GENERAL_REGS All general purpose registers + ADDR_REGS All general purpose registers except %r0 + (These registers can be used in address generation) + FP_REGS All floating point registers + CC_REGS The condition code register + ACCESS_REGS The access registers + + GENERAL_FP_REGS Union of GENERAL_REGS and FP_REGS + ADDR_FP_REGS Union of ADDR_REGS and FP_REGS + GENERAL_CC_REGS Union of GENERAL_REGS and CC_REGS + ADDR_CC_REGS Union of ADDR_REGS and CC_REGS + + NO_REGS No registers + ALL_REGS All registers + + Note that the 'fake' frame pointer and argument pointer registers + are included amongst the address registers here. */ + +enum reg_class +{ + NO_REGS, CC_REGS, ADDR_REGS, GENERAL_REGS, ACCESS_REGS, + ADDR_CC_REGS, GENERAL_CC_REGS, + FP_REGS, ADDR_FP_REGS, GENERAL_FP_REGS, + ALL_REGS, LIM_REG_CLASSES +}; +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +#define REG_CLASS_NAMES \ +{ "NO_REGS", "CC_REGS", "ADDR_REGS", "GENERAL_REGS", "ACCESS_REGS", \ + "ADDR_CC_REGS", "GENERAL_CC_REGS", \ + "FP_REGS", "ADDR_FP_REGS", "GENERAL_FP_REGS", "ALL_REGS" } + +/* Class -> register mapping. */ +#define REG_CLASS_CONTENTS \ +{ \ + { 0x00000000, 0x00000000 }, /* NO_REGS */ \ + { 0x00000000, 0x00000002 }, /* CC_REGS */ \ + { 0x0000fffe, 0x0000000d }, /* ADDR_REGS */ \ + { 0x0000ffff, 0x0000000d }, /* GENERAL_REGS */ \ + { 0x00000000, 0x00000030 }, /* ACCESS_REGS */ \ + { 0x0000fffe, 0x0000000f }, /* ADDR_CC_REGS */ \ + { 0x0000ffff, 0x0000000f }, /* GENERAL_CC_REGS */ \ + { 0xffff0000, 0x00000000 }, /* FP_REGS */ \ + { 0xfffffffe, 0x0000000d }, /* ADDR_FP_REGS */ \ + { 0xffffffff, 0x0000000d }, /* GENERAL_FP_REGS */ \ + { 0xffffffff, 0x0000003f }, /* ALL_REGS */ \ +} + +/* The following macro defines cover classes for Integrated Register + Allocator. Cover classes is a set of non-intersected register + classes covering all hard registers used for register allocation + purpose. Any move between two registers of a cover class should be + cheaper than load or store of the registers. The macro value is + array of register classes with LIM_REG_CLASSES used as the end + marker. */ + +#define IRA_COVER_CLASSES \ +{ \ + GENERAL_REGS, FP_REGS, CC_REGS, ACCESS_REGS, LIM_REG_CLASSES \ +} + +/* In some case register allocation order is not enough for IRA to + generate a good code. The following macro (if defined) increases + cost of REGNO for a pseudo approximately by pseudo usage frequency + multiplied by the macro value. + + We avoid usage of BASE_REGNUM by nonzero macro value because the + reload can decide not to use the hard register because some + constant was forced to be in memory. */ +#define IRA_HARD_REGNO_ADD_COST_MULTIPLIER(regno) \ + (regno == BASE_REGNUM ? 0.0 : 0.5) + +/* Register -> class mapping. */ +extern const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER]; +#define REGNO_REG_CLASS(REGNO) (regclass_map[REGNO]) + +/* ADDR_REGS can be used as base or index register. */ +#define INDEX_REG_CLASS ADDR_REGS +#define BASE_REG_CLASS ADDR_REGS + +/* Check whether REGNO is a hard register of the suitable class + or a pseudo register currently allocated to one such. */ +#define REGNO_OK_FOR_INDEX_P(REGNO) \ + (((REGNO) < FIRST_PSEUDO_REGISTER \ + && REGNO_REG_CLASS ((REGNO)) == ADDR_REGS) \ + || ADDR_REGNO_P (reg_renumber[REGNO])) +#define REGNO_OK_FOR_BASE_P(REGNO) REGNO_OK_FOR_INDEX_P (REGNO) + + +/* We need secondary memory to move data between GPRs and FPRs. With + DFP the ldgr lgdr instructions are available. But these + instructions do not handle GPR pairs so it is not possible for 31 + bit. */ +#define SECONDARY_MEMORY_NEEDED(CLASS1, CLASS2, MODE) \ + ((CLASS1) != (CLASS2) \ + && ((CLASS1) == FP_REGS || (CLASS2) == FP_REGS) \ + && (!TARGET_DFP || !TARGET_64BIT || GET_MODE_SIZE (MODE) != 8)) + +/* Get_secondary_mem widens its argument to BITS_PER_WORD which loses on 64bit + because the movsi and movsf patterns don't handle r/f moves. */ +#define SECONDARY_MEMORY_NEEDED_MODE(MODE) \ + (GET_MODE_BITSIZE (MODE) < 32 \ + ? mode_for_size (32, GET_MODE_CLASS (MODE), 0) \ + : MODE) + + +/* Stack layout and calling conventions. */ + +/* Our stack grows from higher to lower addresses. However, local variables + are accessed by positive offsets, and function arguments are stored at + increasing addresses. */ +#define STACK_GROWS_DOWNWARD +#define FRAME_GROWS_DOWNWARD 1 +/* #undef ARGS_GROW_DOWNWARD */ + +/* The basic stack layout looks like this: the stack pointer points + to the register save area for called functions. Above that area + is the location to place outgoing arguments. Above those follow + dynamic allocations (alloca), and finally the local variables. */ + +/* Offset from stack-pointer to first location of outgoing args. */ +#define STACK_POINTER_OFFSET (TARGET_64BIT ? 160 : 96) + +/* Offset within stack frame to start allocating local variables at. */ +#define STARTING_FRAME_OFFSET 0 + +/* Offset from the stack pointer register to an item dynamically + allocated on the stack, e.g., by `alloca'. */ +#define STACK_DYNAMIC_OFFSET(FUNDECL) \ + (STACK_POINTER_OFFSET + crtl->outgoing_args_size) + +/* Offset of first parameter from the argument pointer register value. + We have a fake argument pointer register that points directly to + the argument area. */ +#define FIRST_PARM_OFFSET(FNDECL) 0 + +/* Defining this macro makes __builtin_frame_address(0) and + __builtin_return_address(0) work with -fomit-frame-pointer. */ +#define INITIAL_FRAME_ADDRESS_RTX \ + (plus_constant (arg_pointer_rtx, -STACK_POINTER_OFFSET)) + +/* The return address of the current frame is retrieved + from the initial value of register RETURN_REGNUM. + For frames farther back, we use the stack slot where + the corresponding RETURN_REGNUM register was saved. */ +#define DYNAMIC_CHAIN_ADDRESS(FRAME) \ + (TARGET_PACKED_STACK ? \ + plus_constant ((FRAME), STACK_POINTER_OFFSET - UNITS_PER_LONG) : (FRAME)) + +/* For -mpacked-stack this adds 160 - 8 (96 - 4) to the output of + builtin_frame_address. Otherwise arg pointer - + STACK_POINTER_OFFSET would be returned for + __builtin_frame_address(0) what might result in an address pointing + somewhere into the middle of the local variables since the packed + stack layout generally does not need all the bytes in the register + save area. */ +#define FRAME_ADDR_RTX(FRAME) \ + DYNAMIC_CHAIN_ADDRESS ((FRAME)) + +#define RETURN_ADDR_RTX(COUNT, FRAME) \ + s390_return_addr_rtx ((COUNT), DYNAMIC_CHAIN_ADDRESS ((FRAME))) + +/* In 31-bit mode, we need to mask off the high bit of return addresses. */ +#define MASK_RETURN_ADDR (TARGET_64BIT ? constm1_rtx : GEN_INT (0x7fffffff)) + + +/* Exception handling. */ + +/* Describe calling conventions for DWARF-2 exception handling. */ +#define INCOMING_RETURN_ADDR_RTX gen_rtx_REG (Pmode, RETURN_REGNUM) +#define INCOMING_FRAME_SP_OFFSET STACK_POINTER_OFFSET +#define DWARF_FRAME_RETURN_COLUMN 14 + +/* Describe how we implement __builtin_eh_return. */ +#define EH_RETURN_DATA_REGNO(N) ((N) < 4 ? (N) + 6 : INVALID_REGNUM) +#define EH_RETURN_HANDLER_RTX gen_rtx_MEM (Pmode, return_address_pointer_rtx) + +/* Select a format to encode pointers in exception handling data. */ +#define ASM_PREFERRED_EH_DATA_FORMAT(CODE, GLOBAL) \ + (flag_pic \ + ? ((GLOBAL) ? DW_EH_PE_indirect : 0) | DW_EH_PE_pcrel | DW_EH_PE_sdata4 \ + : DW_EH_PE_absptr) + +/* Register save slot alignment. */ +#define DWARF_CIE_DATA_ALIGNMENT (-UNITS_PER_LONG) + + +/* Frame registers. */ + +#define STACK_POINTER_REGNUM 15 +#define FRAME_POINTER_REGNUM 34 +#define HARD_FRAME_POINTER_REGNUM 11 +#define ARG_POINTER_REGNUM 32 +#define RETURN_ADDRESS_POINTER_REGNUM 35 + +/* The static chain must be call-clobbered, but not used for + function argument passing. As register 1 is clobbered by + the trampoline code, we only have one option. */ +#define STATIC_CHAIN_REGNUM 0 + +/* Number of hardware registers that go into the DWARF-2 unwind info. + To avoid ABI incompatibility, this number must not change even as + 'fake' hard registers are added or removed. */ +#define DWARF_FRAME_REGISTERS 34 + + +/* Frame pointer and argument pointer elimination. */ + +#define ELIMINABLE_REGS \ +{{ FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ + { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }, \ + { ARG_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ + { ARG_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }, \ + { RETURN_ADDRESS_POINTER_REGNUM, STACK_POINTER_REGNUM }, \ + { RETURN_ADDRESS_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM }, \ + { BASE_REGNUM, BASE_REGNUM }} + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + (OFFSET) = s390_initial_elimination_offset ((FROM), (TO)) + + +/* Stack arguments. */ + +/* We need current_function_outgoing_args to be valid. */ +#define ACCUMULATE_OUTGOING_ARGS 1 + + +/* Register arguments. */ + +typedef struct s390_arg_structure +{ + int gprs; /* gpr so far */ + int fprs; /* fpr so far */ +} +CUMULATIVE_ARGS; + +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, NN, N_NAMED_ARGS) \ + ((CUM).gprs=0, (CUM).fprs=0) + +/* Arguments can be placed in general registers 2 to 6, or in floating + point registers 0 and 2 for 31 bit and fprs 0, 2, 4 and 6 for 64 + bit. */ +#define FUNCTION_ARG_REGNO_P(N) (((N) >=2 && (N) <7) || \ + (N) == 16 || (N) == 17 || (TARGET_64BIT && ((N) == 18 || (N) == 19))) + + +/* Only gpr 2 and fpr 0 are ever used as return registers. */ +#define FUNCTION_VALUE_REGNO_P(N) ((N) == 2 || (N) == 16) + + +/* Function entry and exit. */ + +/* When returning from a function, the stack pointer does not matter. */ +#define EXIT_IGNORE_STACK 1 + + +/* Profiling. */ + +#define FUNCTION_PROFILER(FILE, LABELNO) \ + s390_function_profiler ((FILE), ((LABELNO))) + +#define PROFILE_BEFORE_PROLOGUE 1 + + +/* Trampolines for nested functions. */ + +#define TRAMPOLINE_SIZE (TARGET_64BIT ? 32 : 16) +#define TRAMPOLINE_ALIGNMENT BITS_PER_WORD + +/* Addressing modes, and classification of registers for them. */ + +/* Recognize any constant value that is a valid address. */ +#define CONSTANT_ADDRESS_P(X) 0 + +/* Maximum number of registers that can appear in a valid memory address. */ +#define MAX_REGS_PER_ADDRESS 2 + +/* This definition replaces the formerly used 'm' constraint with a + different constraint letter in order to avoid changing semantics of + the 'm' constraint when accepting new address formats in + TARGET_LEGITIMATE_ADDRESS_P. The constraint letter defined here + must not be used in insn definitions or inline assemblies. */ +#define TARGET_MEM_CONSTRAINT 'e' + +/* Try a machine-dependent way of reloading an illegitimate address + operand. If we find one, push the reload and jump to WIN. This + macro is used in only one place: `find_reloads_address' in reload.c. */ +#define LEGITIMIZE_RELOAD_ADDRESS(AD, MODE, OPNUM, TYPE, IND, WIN) \ +do { \ + rtx new_rtx = legitimize_reload_address (AD, MODE, OPNUM, (int)(TYPE)); \ + if (new_rtx) \ + { \ + (AD) = new_rtx; \ + goto WIN; \ + } \ +} while (0) + +/* Nonzero if the constant value X is a legitimate general operand. + It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */ +#define LEGITIMATE_CONSTANT_P(X) \ + legitimate_constant_p (X) + +/* Helper macro for s390.c and s390.md to check for symbolic constants. */ +#define SYMBOLIC_CONST(X) \ +(GET_CODE (X) == SYMBOL_REF \ + || GET_CODE (X) == LABEL_REF \ + || (GET_CODE (X) == CONST && symbolic_reference_mentioned_p (X))) + +#define TLS_SYMBOLIC_CONST(X) \ +((GET_CODE (X) == SYMBOL_REF && tls_symbolic_operand (X)) \ + || (GET_CODE (X) == CONST && tls_symbolic_reference_mentioned_p (X))) + + +/* Condition codes. */ + +/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE, + return the mode to be used for the comparison. */ +#define SELECT_CC_MODE(OP, X, Y) s390_select_ccmode ((OP), (X), (Y)) + +/* Canonicalize a comparison from one we don't have to one we do have. */ +#define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \ + s390_canonicalize_comparison (&(CODE), &(OP0), &(OP1)) + +/* Relative costs of operations. */ + +/* A C expression for the cost of a branch instruction. A value of 1 + is the default; other values are interpreted relative to that. */ +#define BRANCH_COST(speed_p, predictable_p) 1 + +/* Nonzero if access to memory by bytes is slow and undesirable. */ +#define SLOW_BYTE_ACCESS 1 + +/* An integer expression for the size in bits of the largest integer machine + mode that should actually be used. We allow pairs of registers. */ +#define MAX_FIXED_MODE_SIZE GET_MODE_BITSIZE (TARGET_64BIT ? TImode : DImode) + +/* The maximum number of bytes that a single instruction can move quickly + between memory and registers or between two memory locations. */ +#define MOVE_MAX (TARGET_ZARCH ? 16 : 8) +#define MOVE_MAX_PIECES (TARGET_ZARCH ? 8 : 4) +#define MAX_MOVE_MAX 16 + +/* Determine whether to use move_by_pieces or block move insn. */ +#define MOVE_BY_PIECES_P(SIZE, ALIGN) \ + ( (SIZE) == 1 || (SIZE) == 2 || (SIZE) == 4 \ + || (TARGET_ZARCH && (SIZE) == 8) ) + +/* Determine whether to use clear_by_pieces or block clear insn. */ +#define CLEAR_BY_PIECES_P(SIZE, ALIGN) \ + ( (SIZE) == 1 || (SIZE) == 2 || (SIZE) == 4 \ + || (TARGET_ZARCH && (SIZE) == 8) ) + +/* This macro is used to determine whether store_by_pieces should be + called to "memcpy" storage when the source is a constant string. */ +#define STORE_BY_PIECES_P(SIZE, ALIGN) MOVE_BY_PIECES_P (SIZE, ALIGN) + +/* Likewise to decide whether to "memset" storage with byte values + other than zero. */ +#define SET_BY_PIECES_P(SIZE, ALIGN) STORE_BY_PIECES_P (SIZE, ALIGN) + +/* Don't perform CSE on function addresses. */ +#define NO_FUNCTION_CSE + +/* This value is used in tree-sra to decide whether it might benefical + to split a struct move into several word-size moves. For S/390 + only small values make sense here since struct moves are relatively + cheap thanks to mvc so the small default value choosen for archs + with memmove patterns should be ok. But this value is multiplied + in tree-sra with UNITS_PER_WORD to make a decision so we adjust it + here to compensate for that factor since mvc costs exactly the same + on 31 and 64 bit. */ +#define MOVE_RATIO(speed) (TARGET_64BIT? 2 : 4) + + +/* Sections. */ + +/* Output before read-only data. */ +#define TEXT_SECTION_ASM_OP ".text" + +/* Output before writable (initialized) data. */ +#define DATA_SECTION_ASM_OP ".data" + +/* Output before writable (uninitialized) data. */ +#define BSS_SECTION_ASM_OP ".bss" + +/* S/390 constant pool breaks the devices in crtstuff.c to control section + in where code resides. We have to write it as asm code. */ +#ifndef __s390x__ +#define CRT_CALL_STATIC_FUNCTION(SECTION_OP, FUNC) \ + asm (SECTION_OP "\n\ + bras\t%r2,1f\n\ +0: .long\t" USER_LABEL_PREFIX #FUNC " - 0b\n\ +1: l\t%r3,0(%r2)\n\ + bas\t%r14,0(%r3,%r2)\n\ + .previous"); +#endif + + +/* Position independent code. */ + +#define PIC_OFFSET_TABLE_REGNUM (flag_pic ? 12 : INVALID_REGNUM) + +#define LEGITIMATE_PIC_OPERAND_P(X) legitimate_pic_operand_p (X) + + +/* Assembler file format. */ + +/* Character to start a comment. */ +#define ASM_COMMENT_START "#" + +/* Declare an uninitialized external linkage data object. */ +#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \ + asm_output_aligned_bss (FILE, DECL, NAME, SIZE, ALIGN) + +/* Globalizing directive for a label. */ +#define GLOBAL_ASM_OP ".globl " + +/* Advance the location counter to a multiple of 2**LOG bytes. */ +#define ASM_OUTPUT_ALIGN(FILE, LOG) \ + if ((LOG)) fprintf ((FILE), "\t.align\t%d\n", 1 << (LOG)) + +/* Advance the location counter by SIZE bytes. */ +#define ASM_OUTPUT_SKIP(FILE, SIZE) \ + fprintf ((FILE), "\t.set\t.,.+"HOST_WIDE_INT_PRINT_UNSIGNED"\n", (SIZE)) + +/* The LOCAL_LABEL_PREFIX variable is used by dbxelf.h. */ +#define LOCAL_LABEL_PREFIX "." + +#define LABEL_ALIGN(LABEL) \ + s390_label_align (LABEL) + +/* How to refer to registers in assembler output. This sequence is + indexed by compiler's hard-register-number (see above). */ +#define REGISTER_NAMES \ +{ "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", \ + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", \ + "%f0", "%f2", "%f4", "%f6", "%f1", "%f3", "%f5", "%f7", \ + "%f8", "%f10", "%f12", "%f14", "%f9", "%f11", "%f13", "%f15", \ + "%ap", "%cc", "%fp", "%rp", "%a0", "%a1" \ +} + +/* Print operand X (an rtx) in assembler syntax to file FILE. */ +#define PRINT_OPERAND(FILE, X, CODE) print_operand (FILE, X, CODE) +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR) + +/* Output an element of a case-vector that is absolute. */ +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ +do { \ + char buf[32]; \ + fputs (integer_asm_op (UNITS_PER_LONG, TRUE), (FILE)); \ + ASM_GENERATE_INTERNAL_LABEL (buf, "L", (VALUE)); \ + assemble_name ((FILE), buf); \ + fputc ('\n', (FILE)); \ +} while (0) + +/* Output an element of a case-vector that is relative. */ +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ +do { \ + char buf[32]; \ + fputs (integer_asm_op (UNITS_PER_LONG, TRUE), (FILE)); \ + ASM_GENERATE_INTERNAL_LABEL (buf, "L", (VALUE)); \ + assemble_name ((FILE), buf); \ + fputc ('-', (FILE)); \ + ASM_GENERATE_INTERNAL_LABEL (buf, "L", (REL)); \ + assemble_name ((FILE), buf); \ + fputc ('\n', (FILE)); \ +} while (0) + + +/* Miscellaneous parameters. */ + +/* Specify the machine mode that this machine uses for the index in the + tablejump instruction. */ +#define CASE_VECTOR_MODE (TARGET_64BIT ? DImode : SImode) + +/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits + is done just by pretending it is already truncated. */ +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +/* Specify the machine mode that pointers have. + After generation of rtl, the compiler makes no further distinction + between pointers and any other objects of this machine mode. */ +#define Pmode ((enum machine_mode) (TARGET_64BIT ? DImode : SImode)) + +/* This is -1 for "pointer mode" extend. See ptr_extend in s390.md. */ +#define POINTERS_EXTEND_UNSIGNED -1 + +/* A function address in a call instruction is a byte address (for + indexing purposes) so give the MEM rtx a byte's mode. */ +#define FUNCTION_MODE QImode + +/* Specify the value which is used when clz operand is zero. */ +#define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) ((VALUE) = 64, 1) + +/* Machine-specific symbol_ref flags. */ +#define SYMBOL_FLAG_ALIGN1 (SYMBOL_FLAG_MACH_DEP << 0) +#define SYMBOL_REF_ALIGN1_P(X) \ + ((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_ALIGN1)) +#define SYMBOL_FLAG_NOT_NATURALLY_ALIGNED (SYMBOL_FLAG_MACH_DEP << 1) +#define SYMBOL_REF_NOT_NATURALLY_ALIGNED_P(X) \ + ((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_NOT_NATURALLY_ALIGNED)) + +/* Check whether integer displacement is in range. */ +#define DISP_IN_RANGE(d) \ + (TARGET_LONG_DISPLACEMENT? ((d) >= -524288 && (d) <= 524287) \ + : ((d) >= 0 && (d) <= 4095)) + +/* Reads can reuse write prefetches, used by tree-ssa-prefetch-loops.c. */ +#define READ_CAN_USE_WRITE_PREFETCH 1 +#endif diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md new file mode 100644 index 000000000..ac5b7a014 --- /dev/null +++ b/gcc/config/s390/s390.md @@ -0,0 +1,9410 @@ +;;- Machine description for GNU compiler -- S/390 / zSeries version. +;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +;; 2009, 2010 Free Software Foundation, Inc. +;; Contributed by Hartmut Penner (hpenner@de.ibm.com) and +;; Ulrich Weigand (uweigand@de.ibm.com) and +;; Andreas Krebbel (Andreas.Krebbel@de.ibm.com) + +;; 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 constraints.md for a description of constraints specific to s390. +;; + +;; Special formats used for outputting 390 instructions. +;; +;; %C: print opcode suffix for branch condition. +;; %D: print opcode suffix for inverse branch condition. +;; %J: print tls_load/tls_gdcall/tls_ldcall suffix +;; %G: print the size of the operand in bytes. +;; %O: print only the displacement of a memory reference. +;; %R: print only the base register of a memory reference. +;; %S: print S-type memory reference (base+displacement). +;; %N: print the second word of a DImode operand. +;; %M: print the second word of a TImode operand. +;; %Y: print shift count operand. +;; +;; %b: print integer X as if it's an unsigned byte. +;; %c: print integer X as if it's an signed byte. +;; %x: print integer X as if it's an unsigned halfword. +;; %h: print integer X as if it's a signed halfword. +;; %i: print the first nonzero HImode part of X. +;; %j: print the first HImode part unequal to -1 of X. +;; %k: print the first nonzero SImode part of X. +;; %m: print the first SImode part unequal to -1 of X. +;; %o: print integer X as if it's an unsigned 32bit word. +;; +;; We have a special constraint for pattern matching. +;; +;; s_operand -- Matches a valid S operand in a RS, SI or SS type instruction. +;; + +;; +;; UNSPEC usage +;; + +(define_constants + [; Miscellaneous + (UNSPEC_ROUND 1) + (UNSPEC_CCU_TO_INT 2) + (UNSPEC_CCZ_TO_INT 3) + (UNSPEC_ICM 10) + (UNSPEC_TIE 11) + + ; GOT/PLT and lt-relative accesses + (UNSPEC_LTREL_OFFSET 100) + (UNSPEC_LTREL_BASE 101) + (UNSPEC_POOL_OFFSET 102) + (UNSPEC_GOTENT 110) + (UNSPEC_GOT 111) + (UNSPEC_GOTOFF 112) + (UNSPEC_PLT 113) + (UNSPEC_PLTOFF 114) + + ; Literal pool + (UNSPEC_RELOAD_BASE 210) + (UNSPEC_MAIN_BASE 211) + (UNSPEC_LTREF 212) + (UNSPEC_INSN 213) + (UNSPEC_EXECUTE 214) + + ; Atomic Support + (UNSPEC_MB 400) + + ; TLS relocation specifiers + (UNSPEC_TLSGD 500) + (UNSPEC_TLSLDM 501) + (UNSPEC_NTPOFF 502) + (UNSPEC_DTPOFF 503) + (UNSPEC_GOTNTPOFF 504) + (UNSPEC_INDNTPOFF 505) + + ; TLS support + (UNSPEC_TLSLDM_NTPOFF 511) + (UNSPEC_TLS_LOAD 512) + + ; String Functions + (UNSPEC_SRST 600) + (UNSPEC_MVST 601) + + ; Stack Smashing Protector + (UNSPEC_SP_SET 700) + (UNSPEC_SP_TEST 701) + + ; Test Data Class (TDC) + (UNSPEC_TDC_INSN 800) + + ; Population Count + (UNSPEC_POPCNT 900) + (UNSPEC_COPYSIGN 901) + ]) + +;; +;; UNSPEC_VOLATILE usage +;; + +(define_constants + [; Blockage + (UNSPECV_BLOCKAGE 0) + + ; TPF Support + (UNSPECV_TPF_PROLOGUE 20) + (UNSPECV_TPF_EPILOGUE 21) + + ; Literal pool + (UNSPECV_POOL 200) + (UNSPECV_POOL_SECTION 201) + (UNSPECV_POOL_ALIGN 202) + (UNSPECV_POOL_ENTRY 203) + (UNSPECV_MAIN_POOL 300) + + ; TLS support + (UNSPECV_SET_TP 500) + + ; Atomic Support + (UNSPECV_CAS 700) + (UNSPECV_ATOMIC_OP 701) + ]) + +;; +;; Registers +;; + +; Registers with special meaning + +(define_constants + [ + ; Sibling call register. + (SIBCALL_REGNUM 1) + ; Literal pool base register. + (BASE_REGNUM 13) + ; Return address register. + (RETURN_REGNUM 14) + ; Condition code register. + (CC_REGNUM 33) + ; Thread local storage pointer register. + (TP_REGNUM 36) + ]) + +; Hardware register names + +(define_constants + [ + ; General purpose registers + (GPR0_REGNUM 0) + ; Floating point registers. + (FPR0_REGNUM 16) + (FPR2_REGNUM 18) + ]) + +;; +;; PFPO GPR0 argument format +;; + +(define_constants + [ + ; PFPO operation type + (PFPO_CONVERT 0x1000000) + ; PFPO operand types + (PFPO_OP_TYPE_SF 0x5) + (PFPO_OP_TYPE_DF 0x6) + (PFPO_OP_TYPE_TF 0x7) + (PFPO_OP_TYPE_SD 0x8) + (PFPO_OP_TYPE_DD 0x9) + (PFPO_OP_TYPE_TD 0xa) + ; Bitposition of operand types + (PFPO_OP0_TYPE_SHIFT 16) + (PFPO_OP1_TYPE_SHIFT 8) + ]) + + +;; Instruction operand type as used in the Principles of Operation. +;; Used to determine defaults for length and other attribute values. + +(define_attr "op_type" + "NN,E,RR,RRE,RX,RS,RSI,RI,SI,S,SS,SSE,RXE,RSE,RIL,RIE,RXY,RSY,SIY,RRF,RRR,SIL,RRS,RIS" + (const_string "NN")) + +;; Instruction type attribute used for scheduling. + +(define_attr "type" "none,integer,load,lr,la,larl,lm,stm, + cs,vs,store,sem,idiv, + imulhi,imulsi,imuldi, + branch,jsr,fsimptf,fsimpdf,fsimpsf,fhex, + floadtf,floaddf,floadsf,fstoredf,fstoresf, + fmultf,fmuldf,fmulsf,fdivtf,fdivdf,fdivsf, + ftoi,fsqrttf,fsqrtdf,fsqrtsf, + fmadddf,fmaddsf, + ftrunctf,ftruncdf, ftruncsd, ftruncdd, + itoftf, itofdf, itofsf, itofdd, itoftd, + fdivdd, fdivtd, floaddd, floadsd, fmuldd, fmultd, + fsimpdd, fsimpsd, fsimptd, fstoredd, fstoresd, + ftoidfp, other" + (cond [(eq_attr "op_type" "NN") (const_string "other") + (eq_attr "op_type" "SS") (const_string "cs")] + (const_string "integer"))) + +;; Another attribute used for scheduling purposes: +;; agen: Instruction uses the address generation unit +;; reg: Instruction does not use the agen unit + +(define_attr "atype" "agen,reg" + (if_then_else (eq_attr "op_type" "E,RR,RI,RRE,RSI,RIL,RIE,RRF,RRR") + (const_string "reg") + (const_string "agen"))) + +;; Properties concerning Z10 execution grouping and value forwarding. +;; z10_super: instruction is superscalar. +;; z10_super_c: instruction is superscalar and meets the condition of z10_c. +;; z10_fwd: The instruction reads the value of an operand and stores it into a +;; target register. It can forward this value to a second instruction that reads +;; the same register if that second instruction is issued in the same group. +;; z10_rec: The instruction is in the T pipeline and reads a register. If the +;; instruction in the S pipe writes to the register, then the T instruction +;; can immediately read the new value. +;; z10_fr: union of Z10_fwd and z10_rec. +;; z10_c: second operand of instruction is a register and read with complemented bits. +;; +;; An additional suffix A1, A3, or E1 indicates the respective AGI bypass. + + +(define_attr "z10prop" "none, + z10_super, z10_super_E1, z10_super_A1, z10_super_c, z10_super_c_E1, + z10_fwd, z10_fwd_A1, z10_fwd_A3, z10_fwd_E1, + z10_rec, + z10_fr, z10_fr_A3, z10_fr_E1, + z10_c" + (const_string "none")) + +;; Properties concerning Z196 decoding +;; z196_alone: must group alone +;; z196_end: ends a group +;; z196_cracked: instruction is cracked or expanded +(define_attr "z196prop" "none, + z196_alone, z196_ends, + z196_cracked" + (const_string "none")) + +;; Length in bytes. + +(define_attr "length" "" + (cond [(eq_attr "op_type" "E,RR") (const_int 2) + (eq_attr "op_type" "RX,RI,RRE,RS,RSI,S,SI,RRF,RRR") (const_int 4)] + (const_int 6))) + + +;; Processor type. This attribute must exactly match the processor_type +;; enumeration in s390.h. The current machine description does not +;; distinguish between g5 and g6, but there are differences between the two +;; CPUs could in theory be modeled. + +(define_attr "cpu" "g5,g6,z900,z990,z9_109,z10,z196" + (const (symbol_ref "s390_tune_attr"))) + +(define_attr "cpu_facility" "standard,ieee,zarch,longdisp,extimm,dfp,z10,z196" + (const_string "standard")) + +(define_attr "enabled" "" + (cond [(eq_attr "cpu_facility" "standard") + (const_int 1) + + (and (eq_attr "cpu_facility" "ieee") + (ne (symbol_ref "TARGET_CPU_IEEE_FLOAT") (const_int 0))) + (const_int 1) + + (and (eq_attr "cpu_facility" "zarch") + (ne (symbol_ref "TARGET_ZARCH") (const_int 0))) + (const_int 1) + + (and (eq_attr "cpu_facility" "longdisp") + (ne (symbol_ref "TARGET_LONG_DISPLACEMENT") (const_int 0))) + (const_int 1) + + (and (eq_attr "cpu_facility" "extimm") + (ne (symbol_ref "TARGET_EXTIMM") (const_int 0))) + (const_int 1) + + (and (eq_attr "cpu_facility" "dfp") + (ne (symbol_ref "TARGET_DFP") (const_int 0))) + (const_int 1) + + (and (eq_attr "cpu_facility" "z10") + (ne (symbol_ref "TARGET_Z10") (const_int 0))) + (const_int 1) + + (and (eq_attr "cpu_facility" "z196") + (ne (symbol_ref "TARGET_Z196") (const_int 0))) + (const_int 1)] + (const_int 0))) + +;; Pipeline description for z900. For lack of anything better, +;; this description is also used for the g5 and g6. +(include "2064.md") + +;; Pipeline description for z990, z9-109 and z9-ec. +(include "2084.md") + +;; Pipeline description for z10 +(include "2097.md") + +;; Pipeline description for z196 +(include "2817.md") + +;; Predicates +(include "predicates.md") + +;; Constraint definitions +(include "constraints.md") + +;; Other includes +(include "tpf.md") + +;; Iterators + +;; These mode iterators allow floating point patterns to be generated from the +;; same template. +(define_mode_iterator FP_ALL [TF DF SF (TD "TARGET_HARD_DFP") (DD "TARGET_HARD_DFP") + (SD "TARGET_HARD_DFP")]) +(define_mode_iterator FP [TF DF SF (TD "TARGET_HARD_DFP") (DD "TARGET_HARD_DFP")]) +(define_mode_iterator FPALL [TF DF SF TD DD SD]) +(define_mode_iterator BFP [TF DF SF]) +(define_mode_iterator DFP [TD DD]) +(define_mode_iterator DFP_ALL [TD DD SD]) +(define_mode_iterator DSF [DF SF]) +(define_mode_iterator SD_SF [SF SD]) +(define_mode_iterator DD_DF [DF DD]) +(define_mode_iterator TD_TF [TF TD]) + +;; This mode iterator allows 31-bit and 64-bit TDSI patterns to be generated +;; from the same template. +(define_mode_iterator TDSI [(TI "TARGET_64BIT") DI SI]) + +;; These mode iterators allow 31-bit and 64-bit GPR patterns to be generated +;; from the same template. +(define_mode_iterator GPR [(DI "TARGET_ZARCH") SI]) +(define_mode_iterator DSI [DI SI]) + +;; These mode iterators allow :P to be used for patterns that operate on +;; pointer-sized quantities. Exactly one of the two alternatives will match. +(define_mode_iterator P [(DI "TARGET_64BIT") (SI "!TARGET_64BIT")]) + +;; These macros refer to the actual word_mode of the configuration. This is equal +;; to Pmode except on 31-bit machines in zarch mode. +(define_mode_iterator DW [(TI "TARGET_ZARCH") (DI "!TARGET_ZARCH")]) +(define_mode_iterator W [(DI "TARGET_ZARCH") (SI "!TARGET_ZARCH")]) + +;; This mode iterator allows the QI and HI patterns to be defined from +;; the same template. +(define_mode_iterator HQI [HI QI]) + +;; This mode iterator allows the integer patterns to be defined from the +;; same template. +(define_mode_iterator INT [(DI "TARGET_ZARCH") SI HI QI]) +(define_mode_iterator INTALL [TI DI SI HI QI]) + +;; This iterator allows some 'ashift' and 'lshiftrt' pattern to be defined from +;; the same template. +(define_code_iterator SHIFT [ashift lshiftrt]) + +;; This iterator and attribute allow to combine most atomic operations. +(define_code_iterator ATOMIC [and ior xor plus minus mult]) +(define_code_iterator ATOMIC_Z196 [and ior xor plus]) +(define_code_attr atomic [(and "and") (ior "ior") (xor "xor") + (plus "add") (minus "sub") (mult "nand")]) +(define_code_attr noxa [(and "n") (ior "o") (xor "x") (plus "a")]) + +;; In FP templates, a string like "lt<de>br" will expand to "ltxbr" in +;; TF/TDmode, "ltdbr" in DF/DDmode, and "ltebr" in SF/SDmode. +(define_mode_attr xde [(TF "x") (DF "d") (SF "e") (TD "x") (DD "d") (SD "e")]) + +;; In FP templates, a <dee> in "m<dee><bt>r" will expand to "mx<bt>r" in +;; TF/TDmode, "md<bt>r" in DF/DDmode, "mee<bt>r" in SFmode and "me<bt>r in +;; SDmode. +(define_mode_attr xdee [(TF "x") (DF "d") (SF "ee") (TD "x") (DD "d") (SD "e")]) + +;; In FP templates, "<RRe>" will expand to "RRE" in TFmode and "RR" otherwise. +;; Likewise for "<RXe>". +(define_mode_attr RRe [(TF "RRE") (DF "RR") (SF "RR")]) +(define_mode_attr RXe [(TF "RXE") (DF "RX") (SF "RX")]) + +;; The decimal floating point variants of add, sub, div and mul support 3 +;; fp register operands. The following attributes allow to merge the bfp and +;; dfp variants in a single insn definition. + +;; This attribute is used to set op_type accordingly. +(define_mode_attr RRer [(TF "RRE") (DF "RRE") (SF "RRE") (TD "RRR") + (DD "RRR") (SD "RRR")]) + +;; This attribute is used in the operand constraint list in order to have the +;; first and the second operand match for bfp modes. +(define_mode_attr f0 [(TF "0") (DF "0") (SF "0") (TD "f") (DD "f") (DD "f")]) + +;; This attribute is used in the operand list of the instruction to have an +;; additional operand for the dfp instructions. +(define_mode_attr op1 [(TF "") (DF "") (SF "") + (TD "%1,") (DD "%1,") (SD "%1,")]) + + +;; This attribute is used in the operand constraint list +;; for instructions dealing with the sign bit of 32 or 64bit fp values. +;; TFmode values are represented by a fp register pair. Since the +;; sign bit instructions only handle single source and target fp registers +;; these instructions can only be used for TFmode values if the source and +;; target operand uses the same fp register. +(define_mode_attr fT0 [(TF "0") (DF "f") (SF "f")]) + +;; In FP templates, "<Rf>" will expand to "f" in TFmode and "R" otherwise. +;; This is used to disable the memory alternative in TFmode patterns. +(define_mode_attr Rf [(TF "f") (DF "R") (SF "R") (TD "f") (DD "f") (SD "f")]) + +;; This attribute adds b for bfp instructions and t for dfp instructions and is used +;; within instruction mnemonics. +(define_mode_attr bt [(TF "b") (DF "b") (SF "b") (TD "t") (DD "t") (SD "t")]) + +;; This attribute is used within instruction mnemonics. It evaluates to d for dfp +;; modes and to an empty string for bfp modes. +(define_mode_attr _d [(TF "") (DF "") (SF "") (TD "d") (DD "d") (SD "d")]) + +;; In GPR and P templates, a constraint like "<d0>" will expand to "d" in DImode +;; and "0" in SImode. This allows to combine instructions of which the 31bit +;; version only operates on one register. +(define_mode_attr d0 [(DI "d") (SI "0")]) + +;; In combination with d0 this allows to combine instructions of which the 31bit +;; version only operates on one register. The DImode version needs an additional +;; register for the assembler output. +(define_mode_attr 1 [(DI "%1,") (SI "")]) + +;; In SHIFT templates, a string like "s<lr>dl" will expand to "sldl" in +;; 'ashift' and "srdl" in 'lshiftrt'. +(define_code_attr lr [(ashift "l") (lshiftrt "r")]) + +;; In SHIFT templates, this attribute holds the correct standard name for the +;; pattern itself and the corresponding function calls. +(define_code_attr shift [(ashift "ashl") (lshiftrt "lshr")]) + +;; This attribute handles differences in the instruction 'type' and will result +;; in "RRE" for DImode and "RR" for SImode. +(define_mode_attr E [(DI "E") (SI "")]) + +;; This attribute handles differences in the instruction 'type' and makes RX<Y> +;; to result in "RXY" for DImode and "RX" for SImode. +(define_mode_attr Y [(DI "Y") (SI "")]) + +;; This attribute handles differences in the instruction 'type' and will result +;; in "RSE" for TImode and "RS" for DImode. +(define_mode_attr TE [(TI "E") (DI "")]) + +;; In GPR templates, a string like "lc<g>r" will expand to "lcgr" in DImode +;; and "lcr" in SImode. +(define_mode_attr g [(DI "g") (SI "")]) + +;; In GPR templates, a string like "sl<y>" will expand to "slg" in DImode +;; and "sly" in SImode. This is useful because on 64bit the ..g instructions +;; were enhanced with long displacements whereas 31bit instructions got a ..y +;; variant for long displacements. +(define_mode_attr y [(DI "g") (SI "y")]) + +;; In DW templates, a string like "cds<g>" will expand to "cdsg" in TImode +;; and "cds" in DImode. +(define_mode_attr tg [(TI "g") (DI "")]) + +;; In GPR templates, a string like "c<gf>dbr" will expand to "cgdbr" in DImode +;; and "cfdbr" in SImode. +(define_mode_attr gf [(DI "g") (SI "f")]) + +;; In GPR templates, a string like sll<gk> will expand to sllg for DI +;; and sllk for SI. This way it is possible to merge the new z196 SI +;; 3 operands shift instructions into the existing patterns. +(define_mode_attr gk [(DI "g") (SI "k")]) + +;; ICM mask required to load MODE value into the lowest subreg +;; of a SImode register. +(define_mode_attr icm_lo [(HI "3") (QI "1")]) + +;; In HQI templates, a string like "llg<hc>" will expand to "llgh" in +;; HImode and "llgc" in QImode. +(define_mode_attr hc [(HI "h") (QI "c")]) + +;; In P templates, the mode <DBL> will expand to "TI" in DImode and "DI" +;; in SImode. +(define_mode_attr DBL [(DI "TI") (SI "DI")]) + +;; This attribute expands to DF for TFmode and to DD for TDmode . It is +;; used for Txmode splitters splitting a Txmode copy into 2 Dxmode copies. +(define_mode_attr HALF_TMODE [(TF "DF") (TD "DD")]) + +;; Maximum unsigned integer that fits in MODE. +(define_mode_attr max_uint [(HI "65535") (QI "255")]) + +;; +;;- Compare instructions. +;; + +; Test-under-Mask instructions + +(define_insn "*tmqi_mem" + [(set (reg CC_REGNUM) + (compare (and:QI (match_operand:QI 0 "memory_operand" "Q,S") + (match_operand:QI 1 "immediate_operand" "n,n")) + (match_operand:QI 2 "immediate_operand" "n,n")))] + "s390_match_ccmode (insn, s390_tm_ccmode (operands[1], operands[2], false))" + "@ + tm\t%S0,%b1 + tmy\t%S0,%b1" + [(set_attr "op_type" "SI,SIY") + (set_attr "z10prop" "z10_super,z10_super")]) + +(define_insn "*tmdi_reg" + [(set (reg CC_REGNUM) + (compare (and:DI (match_operand:DI 0 "nonimmediate_operand" "d,d,d,d") + (match_operand:DI 1 "immediate_operand" + "N0HD0,N1HD0,N2HD0,N3HD0")) + (match_operand:DI 2 "immediate_operand" "n,n,n,n")))] + "TARGET_ZARCH + && s390_match_ccmode (insn, s390_tm_ccmode (operands[1], operands[2], true)) + && s390_single_part (operands[1], DImode, HImode, 0) >= 0" + "@ + tmhh\t%0,%i1 + tmhl\t%0,%i1 + tmlh\t%0,%i1 + tmll\t%0,%i1" + [(set_attr "op_type" "RI") + (set_attr "z10prop" "z10_super,z10_super,z10_super,z10_super")]) + +(define_insn "*tmsi_reg" + [(set (reg CC_REGNUM) + (compare (and:SI (match_operand:SI 0 "nonimmediate_operand" "d,d") + (match_operand:SI 1 "immediate_operand" "N0HS0,N1HS0")) + (match_operand:SI 2 "immediate_operand" "n,n")))] + "s390_match_ccmode (insn, s390_tm_ccmode (operands[1], operands[2], true)) + && s390_single_part (operands[1], SImode, HImode, 0) >= 0" + "@ + tmh\t%0,%i1 + tml\t%0,%i1" + [(set_attr "op_type" "RI") + (set_attr "z10prop" "z10_super,z10_super")]) + +(define_insn "*tm<mode>_full" + [(set (reg CC_REGNUM) + (compare (match_operand:HQI 0 "register_operand" "d") + (match_operand:HQI 1 "immediate_operand" "n")))] + "s390_match_ccmode (insn, s390_tm_ccmode (constm1_rtx, operands[1], true))" + "tml\t%0,<max_uint>" + [(set_attr "op_type" "RI") + (set_attr "z10prop" "z10_super")]) + + +; +; Load-and-Test instructions +; + +; tst(di|si) instruction pattern(s). + +(define_insn "*tstdi_sign" + [(set (reg CC_REGNUM) + (compare + (ashiftrt:DI + (ashift:DI + (subreg:DI (match_operand:SI 0 "nonimmediate_operand" "d,RT") 0) + (const_int 32)) (const_int 32)) + (match_operand:DI 1 "const0_operand" ""))) + (set (match_operand:DI 2 "register_operand" "=d,d") + (sign_extend:DI (match_dup 0)))] + "s390_match_ccmode(insn, CCSmode) && TARGET_ZARCH" + "ltgfr\t%2,%0 + ltgf\t%2,%0" + [(set_attr "op_type" "RRE,RXY") + (set_attr "cpu_facility" "*,z10") + (set_attr "z10prop" "z10_super_E1,z10_super_E1") ]) + +; ltr, lt, ltgr, ltg +(define_insn "*tst<mode>_extimm" + [(set (reg CC_REGNUM) + (compare (match_operand:GPR 0 "nonimmediate_operand" "d,RT") + (match_operand:GPR 1 "const0_operand" ""))) + (set (match_operand:GPR 2 "register_operand" "=d,d") + (match_dup 0))] + "s390_match_ccmode(insn, CCSmode) && TARGET_EXTIMM" + "@ + lt<g>r\t%2,%0 + lt<g>\t%2,%0" + [(set_attr "op_type" "RR<E>,RXY") + (set_attr "z10prop" "z10_fr_E1,z10_fwd_A3") ]) + +; ltr, lt, ltgr, ltg +(define_insn "*tst<mode>_cconly_extimm" + [(set (reg CC_REGNUM) + (compare (match_operand:GPR 0 "nonimmediate_operand" "d,RT") + (match_operand:GPR 1 "const0_operand" ""))) + (clobber (match_scratch:GPR 2 "=X,d"))] + "s390_match_ccmode(insn, CCSmode) && TARGET_EXTIMM" + "@ + lt<g>r\t%0,%0 + lt<g>\t%2,%0" + [(set_attr "op_type" "RR<E>,RXY") + (set_attr "z10prop" "z10_fr_E1,z10_fwd_A3")]) + +(define_insn "*tstdi" + [(set (reg CC_REGNUM) + (compare (match_operand:DI 0 "register_operand" "d") + (match_operand:DI 1 "const0_operand" ""))) + (set (match_operand:DI 2 "register_operand" "=d") + (match_dup 0))] + "s390_match_ccmode(insn, CCSmode) && TARGET_ZARCH && !TARGET_EXTIMM" + "ltgr\t%2,%0" + [(set_attr "op_type" "RRE") + (set_attr "z10prop" "z10_fr_E1")]) + +(define_insn "*tstsi" + [(set (reg CC_REGNUM) + (compare (match_operand:SI 0 "nonimmediate_operand" "d,Q,S") + (match_operand:SI 1 "const0_operand" ""))) + (set (match_operand:SI 2 "register_operand" "=d,d,d") + (match_dup 0))] + "s390_match_ccmode(insn, CCSmode) && !TARGET_EXTIMM" + "@ + ltr\t%2,%0 + icm\t%2,15,%S0 + icmy\t%2,15,%S0" + [(set_attr "op_type" "RR,RS,RSY") + (set_attr "z10prop" "z10_fr_E1,z10_super_E1,z10_super_E1")]) + +(define_insn "*tstsi_cconly" + [(set (reg CC_REGNUM) + (compare (match_operand:SI 0 "nonimmediate_operand" "d,Q,S") + (match_operand:SI 1 "const0_operand" ""))) + (clobber (match_scratch:SI 2 "=X,d,d"))] + "s390_match_ccmode(insn, CCSmode)" + "@ + ltr\t%0,%0 + icm\t%2,15,%S0 + icmy\t%2,15,%S0" + [(set_attr "op_type" "RR,RS,RSY") + (set_attr "z10prop" "z10_fr_E1,z10_super_E1,z10_super_E1")]) + +(define_insn "*tstdi_cconly_31" + [(set (reg CC_REGNUM) + (compare (match_operand:DI 0 "register_operand" "d") + (match_operand:DI 1 "const0_operand" "")))] + "s390_match_ccmode(insn, CCSmode) && !TARGET_ZARCH" + "srda\t%0,0" + [(set_attr "op_type" "RS") + (set_attr "atype" "reg")]) + +; ltr, ltgr +(define_insn "*tst<mode>_cconly2" + [(set (reg CC_REGNUM) + (compare (match_operand:GPR 0 "register_operand" "d") + (match_operand:GPR 1 "const0_operand" "")))] + "s390_match_ccmode(insn, CCSmode)" + "lt<g>r\t%0,%0" + [(set_attr "op_type" "RR<E>") + (set_attr "z10prop" "z10_fr_E1")]) + +; tst(hi|qi) instruction pattern(s). + +(define_insn "*tst<mode>CCT" + [(set (reg CC_REGNUM) + (compare (match_operand:HQI 0 "nonimmediate_operand" "?Q,?S,d") + (match_operand:HQI 1 "const0_operand" ""))) + (set (match_operand:HQI 2 "register_operand" "=d,d,0") + (match_dup 0))] + "s390_match_ccmode(insn, CCTmode)" + "@ + icm\t%2,<icm_lo>,%S0 + icmy\t%2,<icm_lo>,%S0 + tml\t%0,<max_uint>" + [(set_attr "op_type" "RS,RSY,RI") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,z10_super")]) + +(define_insn "*tsthiCCT_cconly" + [(set (reg CC_REGNUM) + (compare (match_operand:HI 0 "nonimmediate_operand" "Q,S,d") + (match_operand:HI 1 "const0_operand" ""))) + (clobber (match_scratch:HI 2 "=d,d,X"))] + "s390_match_ccmode(insn, CCTmode)" + "@ + icm\t%2,3,%S0 + icmy\t%2,3,%S0 + tml\t%0,65535" + [(set_attr "op_type" "RS,RSY,RI") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,z10_super")]) + +(define_insn "*tstqiCCT_cconly" + [(set (reg CC_REGNUM) + (compare (match_operand:QI 0 "nonimmediate_operand" "?Q,?S,d") + (match_operand:QI 1 "const0_operand" "")))] + "s390_match_ccmode(insn, CCTmode)" + "@ + cli\t%S0,0 + cliy\t%S0,0 + tml\t%0,255" + [(set_attr "op_type" "SI,SIY,RI") + (set_attr "z10prop" "z10_super,z10_super,z10_super")]) + +(define_insn "*tst<mode>" + [(set (reg CC_REGNUM) + (compare (match_operand:HQI 0 "s_operand" "Q,S") + (match_operand:HQI 1 "const0_operand" ""))) + (set (match_operand:HQI 2 "register_operand" "=d,d") + (match_dup 0))] + "s390_match_ccmode(insn, CCSmode)" + "@ + icm\t%2,<icm_lo>,%S0 + icmy\t%2,<icm_lo>,%S0" + [(set_attr "op_type" "RS,RSY") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + +(define_insn "*tst<mode>_cconly" + [(set (reg CC_REGNUM) + (compare (match_operand:HQI 0 "s_operand" "Q,S") + (match_operand:HQI 1 "const0_operand" ""))) + (clobber (match_scratch:HQI 2 "=d,d"))] + "s390_match_ccmode(insn, CCSmode)" + "@ + icm\t%2,<icm_lo>,%S0 + icmy\t%2,<icm_lo>,%S0" + [(set_attr "op_type" "RS,RSY") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + + +; Compare (equality) instructions + +(define_insn "*cmpdi_cct" + [(set (reg CC_REGNUM) + (compare (match_operand:DI 0 "nonimmediate_operand" "%d,d,d,d,Q") + (match_operand:DI 1 "general_operand" "d,K,Os,RT,BQ")))] + "s390_match_ccmode (insn, CCTmode) && TARGET_ZARCH" + "@ + cgr\t%0,%1 + cghi\t%0,%h1 + cgfi\t%0,%1 + cg\t%0,%1 + #" + [(set_attr "op_type" "RRE,RI,RIL,RXY,SS") + (set_attr "z10prop" "z10_super_c,z10_super,z10_super,z10_super,*")]) + +(define_insn "*cmpsi_cct" + [(set (reg CC_REGNUM) + (compare (match_operand:SI 0 "nonimmediate_operand" "%d,d,d,d,d,Q") + (match_operand:SI 1 "general_operand" "d,K,Os,R,T,BQ")))] + "s390_match_ccmode (insn, CCTmode)" + "@ + cr\t%0,%1 + chi\t%0,%h1 + cfi\t%0,%1 + c\t%0,%1 + cy\t%0,%1 + #" + [(set_attr "op_type" "RR,RI,RIL,RX,RXY,SS") + (set_attr "z10prop" "z10_super_c,z10_super,z10_super,z10_super,z10_super,*")]) + +; Compare (signed) instructions + +(define_insn "*cmpdi_ccs_sign" + [(set (reg CC_REGNUM) + (compare (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" + "d,RT,b")) + (match_operand:DI 0 "register_operand" "d, d,d")))] + "s390_match_ccmode(insn, CCSRmode) && TARGET_ZARCH" + "@ + cgfr\t%0,%1 + cgf\t%0,%1 + cgfrl\t%0,%1" + [(set_attr "op_type" "RRE,RXY,RIL") + (set_attr "z10prop" "z10_c,*,*") + (set_attr "type" "*,*,larl")]) + + + +(define_insn "*cmpsi_ccs_sign" + [(set (reg CC_REGNUM) + (compare (sign_extend:SI (match_operand:HI 1 "memory_operand" "R,T,b")) + (match_operand:SI 0 "register_operand" "d,d,d")))] + "s390_match_ccmode(insn, CCSRmode)" + "@ + ch\t%0,%1 + chy\t%0,%1 + chrl\t%0,%1" + [(set_attr "op_type" "RX,RXY,RIL") + (set_attr "cpu_facility" "*,*,z10") + (set_attr "type" "*,*,larl") + (set_attr "z196prop" "z196_cracked,z196_cracked,z196_cracked")]) + +(define_insn "*cmphi_ccs_z10" + [(set (reg CC_REGNUM) + (compare (match_operand:HI 0 "s_operand" "Q") + (match_operand:HI 1 "immediate_operand" "K")))] + "s390_match_ccmode(insn, CCSmode) && TARGET_Z10" + "chhsi\t%0,%1" + [(set_attr "op_type" "SIL") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "*cmpdi_ccs_signhi_rl" + [(set (reg CC_REGNUM) + (compare (sign_extend:DI (match_operand:HI 1 "memory_operand" "RT,b")) + (match_operand:GPR 0 "register_operand" "d,d")))] + "s390_match_ccmode(insn, CCSRmode) && TARGET_Z10" + "@ + cgh\t%0,%1 + cghrl\t%0,%1" + [(set_attr "op_type" "RXY,RIL") + (set_attr "type" "*,larl")]) + +; cr, chi, cfi, c, cy, cgr, cghi, cgfi, cg, chsi, cghsi, crl, cgrl +(define_insn "*cmp<mode>_ccs" + [(set (reg CC_REGNUM) + (compare (match_operand:GPR 0 "nonimmediate_operand" + "d,d,Q, d,d,d,d") + (match_operand:GPR 1 "general_operand" + "d,K,K,Os,R,T,b")))] + "s390_match_ccmode(insn, CCSmode)" + "@ + c<g>r\t%0,%1 + c<g>hi\t%0,%h1 + c<g>hsi\t%0,%h1 + c<g>fi\t%0,%1 + c<g>\t%0,%1 + c<y>\t%0,%1 + c<g>rl\t%0,%1" + [(set_attr "op_type" "RR<E>,RI,SIL,RIL,RX<Y>,RXY,RIL") + (set_attr "cpu_facility" "*,*,z10,extimm,*,*,z10") + (set_attr "type" "*,*,*,*,*,*,larl") + (set_attr "z10prop" "z10_super_c,z10_super,z10_super,z10_super,z10_super,z10_super,z10_super")]) + + +; Compare (unsigned) instructions + +(define_insn "*cmpsi_ccu_zerohi_rlsi" + [(set (reg CC_REGNUM) + (compare (zero_extend:SI (mem:HI (match_operand:SI 1 + "larl_operand" "X"))) + (match_operand:SI 0 "register_operand" "d")))] + "s390_match_ccmode(insn, CCURmode) && TARGET_Z10" + "clhrl\t%0,%1" + [(set_attr "op_type" "RIL") + (set_attr "type" "larl") + (set_attr "z10prop" "z10_super")]) + +; clhrl, clghrl +(define_insn "*cmp<GPR:mode>_ccu_zerohi_rldi" + [(set (reg CC_REGNUM) + (compare (zero_extend:GPR (mem:HI (match_operand:DI 1 + "larl_operand" "X"))) + (match_operand:GPR 0 "register_operand" "d")))] + "s390_match_ccmode(insn, CCURmode) && TARGET_Z10" + "cl<g>hrl\t%0,%1" + [(set_attr "op_type" "RIL") + (set_attr "type" "larl") + (set_attr "z10prop" "z10_super")]) + +(define_insn "*cmpdi_ccu_zero" + [(set (reg CC_REGNUM) + (compare (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" + "d,RT,b")) + (match_operand:DI 0 "register_operand" "d, d,d")))] + "s390_match_ccmode (insn, CCURmode) && TARGET_ZARCH" + "@ + clgfr\t%0,%1 + clgf\t%0,%1 + clgfrl\t%0,%1" + [(set_attr "op_type" "RRE,RXY,RIL") + (set_attr "cpu_facility" "*,*,z10") + (set_attr "type" "*,*,larl") + (set_attr "z10prop" "z10_super_c,z10_super_E1,z10_super")]) + +(define_insn "*cmpdi_ccu" + [(set (reg CC_REGNUM) + (compare (match_operand:DI 0 "nonimmediate_operand" + "d, d,d,Q, d, Q,BQ") + (match_operand:DI 1 "general_operand" + "d,Op,b,D,RT,BQ,Q")))] + "s390_match_ccmode (insn, CCUmode) && TARGET_ZARCH" + "@ + clgr\t%0,%1 + clgfi\t%0,%1 + clgrl\t%0,%1 + clghsi\t%0,%x1 + clg\t%0,%1 + # + #" + [(set_attr "op_type" "RRE,RIL,RIL,SIL,RXY,SS,SS") + (set_attr "cpu_facility" "*,extimm,z10,z10,*,*,*") + (set_attr "type" "*,*,larl,*,*,*,*") + (set_attr "z10prop" "z10_super_c,z10_super,z10_super,z10_super,z10_super,*,*")]) + +(define_insn "*cmpsi_ccu" + [(set (reg CC_REGNUM) + (compare (match_operand:SI 0 "nonimmediate_operand" "d, d,d,Q,d,d, Q,BQ") + (match_operand:SI 1 "general_operand" "d,Os,b,D,R,T,BQ, Q")))] + "s390_match_ccmode (insn, CCUmode)" + "@ + clr\t%0,%1 + clfi\t%0,%o1 + clrl\t%0,%1 + clfhsi\t%0,%x1 + cl\t%0,%1 + cly\t%0,%1 + # + #" + [(set_attr "op_type" "RR,RIL,RIL,SIL,RX,RXY,SS,SS") + (set_attr "cpu_facility" "*,extimm,z10,z10,*,*,*,*") + (set_attr "type" "*,*,larl,*,*,*,*,*") + (set_attr "z10prop" "z10_super_c,z10_super,z10_super,z10_super,z10_super,z10_super,*,*")]) + +(define_insn "*cmphi_ccu" + [(set (reg CC_REGNUM) + (compare (match_operand:HI 0 "nonimmediate_operand" "d,d,Q,Q,BQ") + (match_operand:HI 1 "general_operand" "Q,S,D,BQ,Q")))] + "s390_match_ccmode (insn, CCUmode) + && !register_operand (operands[1], HImode)" + "@ + clm\t%0,3,%S1 + clmy\t%0,3,%S1 + clhhsi\t%0,%1 + # + #" + [(set_attr "op_type" "RS,RSY,SIL,SS,SS") + (set_attr "cpu_facility" "*,*,z10,*,*") + (set_attr "z10prop" "*,*,z10_super,*,*")]) + +(define_insn "*cmpqi_ccu" + [(set (reg CC_REGNUM) + (compare (match_operand:QI 0 "nonimmediate_operand" "d,d,Q,S,Q,BQ") + (match_operand:QI 1 "general_operand" "Q,S,n,n,BQ,Q")))] + "s390_match_ccmode (insn, CCUmode) + && !register_operand (operands[1], QImode)" + "@ + clm\t%0,1,%S1 + clmy\t%0,1,%S1 + cli\t%S0,%b1 + cliy\t%S0,%b1 + # + #" + [(set_attr "op_type" "RS,RSY,SI,SIY,SS,SS") + (set_attr "z10prop" "*,*,z10_super,z10_super,*,*")]) + + +; Block compare (CLC) instruction patterns. + +(define_insn "*clc" + [(set (reg CC_REGNUM) + (compare (match_operand:BLK 0 "memory_operand" "Q") + (match_operand:BLK 1 "memory_operand" "Q"))) + (use (match_operand 2 "const_int_operand" "n"))] + "s390_match_ccmode (insn, CCUmode) + && INTVAL (operands[2]) >= 1 && INTVAL (operands[2]) <= 256" + "clc\t%O0(%2,%R0),%S1" + [(set_attr "op_type" "SS")]) + +(define_split + [(set (reg CC_REGNUM) + (compare (match_operand 0 "memory_operand" "") + (match_operand 1 "memory_operand" "")))] + "reload_completed + && s390_match_ccmode (insn, CCUmode) + && GET_MODE (operands[0]) == GET_MODE (operands[1]) + && GET_MODE_SIZE (GET_MODE (operands[0])) > 0" + [(parallel + [(set (match_dup 0) (match_dup 1)) + (use (match_dup 2))])] +{ + operands[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (operands[0]))); + operands[0] = adjust_address (operands[0], BLKmode, 0); + operands[1] = adjust_address (operands[1], BLKmode, 0); + + operands[1] = gen_rtx_COMPARE (GET_MODE (SET_DEST (PATTERN (curr_insn))), + operands[0], operands[1]); + operands[0] = SET_DEST (PATTERN (curr_insn)); +}) + + +; (TF|DF|SF|TD|DD|SD) instructions + +; ltxbr, ltdbr, ltebr, ltxtr, ltdtr +(define_insn "*cmp<mode>_ccs_0" + [(set (reg CC_REGNUM) + (compare (match_operand:FP 0 "register_operand" "f") + (match_operand:FP 1 "const0_operand" "")))] + "s390_match_ccmode(insn, CCSmode) && TARGET_HARD_FLOAT" + "lt<xde><bt>r\t%0,%0" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + +; cxtr, cxbr, cdbr, cebr, cdb, ceb, cxbtr, cdbtr +(define_insn "*cmp<mode>_ccs" + [(set (reg CC_REGNUM) + (compare (match_operand:FP 0 "register_operand" "f,f") + (match_operand:FP 1 "general_operand" "f,<Rf>")))] + "s390_match_ccmode(insn, CCSmode) && TARGET_HARD_FLOAT" + "@ + c<xde><bt>r\t%0,%1 + c<xde>b\t%0,%1" + [(set_attr "op_type" "RRE,RXE") + (set_attr "type" "fsimp<mode>")]) + + +; Compare and Branch instructions + +; cij, cgij, crj, cgrj, cfi, cgfi, cr, cgr +; The following instructions do a complementary access of their second +; operand (z01 only): crj_c, cgrjc, cr, cgr +(define_insn "*cmp_and_br_signed_<mode>" + [(set (pc) + (if_then_else (match_operator 0 "s390_signed_integer_comparison" + [(match_operand:GPR 1 "register_operand" "d,d") + (match_operand:GPR 2 "nonmemory_operand" "d,C")]) + (label_ref (match_operand 3 "" "")) + (pc))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_Z10 && !TARGET_AVOID_CMP_AND_BRANCH" +{ + if (get_attr_length (insn) == 6) + return which_alternative ? + "c<g>ij%C0\t%1,%c2,%l3" : "c<g>rj%C0\t%1,%2,%l3"; + else + return which_alternative ? + "c<g>fi\t%1,%c2\;jg%C0\t%l3" : "c<g>r\t%1,%2\;jg%C0\t%l3"; +} + [(set_attr "op_type" "RIE") + (set_attr "type" "branch") + (set_attr "z10prop" "z10_super_c,z10_super") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 3))) (const_int 60000)) + (const_int 6) (const_int 12)))]) ; 8 byte for cr/jg + ; 10 byte for cgr/jg + +; clij, clgij, clrj, clgrj, clfi, clgfi, clr, clgr +; The following instructions do a complementary access of their second +; operand (z10 only): clrj, clgrj, clr, clgr +(define_insn "*cmp_and_br_unsigned_<mode>" + [(set (pc) + (if_then_else (match_operator 0 "s390_unsigned_integer_comparison" + [(match_operand:GPR 1 "register_operand" "d,d") + (match_operand:GPR 2 "nonmemory_operand" "d,I")]) + (label_ref (match_operand 3 "" "")) + (pc))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_Z10 && !TARGET_AVOID_CMP_AND_BRANCH" +{ + if (get_attr_length (insn) == 6) + return which_alternative ? + "cl<g>ij%C0\t%1,%b2,%l3" : "cl<g>rj%C0\t%1,%2,%l3"; + else + return which_alternative ? + "cl<g>fi\t%1,%b2\;jg%C0\t%l3" : "cl<g>r\t%1,%2\;jg%C0\t%l3"; +} + [(set_attr "op_type" "RIE") + (set_attr "type" "branch") + (set_attr "z10prop" "z10_super_c,z10_super") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 3))) (const_int 60000)) + (const_int 6) (const_int 12)))]) ; 8 byte for clr/jg + ; 10 byte for clgr/jg + +; And now the same two patterns as above but with a negated CC mask. + +; cij, cgij, crj, cgrj, cfi, cgfi, cr, cgr +; The following instructions do a complementary access of their second +; operand (z01 only): crj_c, cgrjc, cr, cgr +(define_insn "*icmp_and_br_signed_<mode>" + [(set (pc) + (if_then_else (match_operator 0 "s390_signed_integer_comparison" + [(match_operand:GPR 1 "register_operand" "d,d") + (match_operand:GPR 2 "nonmemory_operand" "d,C")]) + (pc) + (label_ref (match_operand 3 "" "")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_Z10 && !TARGET_AVOID_CMP_AND_BRANCH" +{ + if (get_attr_length (insn) == 6) + return which_alternative ? + "c<g>ij%D0\t%1,%c2,%l3" : "c<g>rj%D0\t%1,%2,%l3"; + else + return which_alternative ? + "c<g>fi\t%1,%c2\;jg%D0\t%l3" : "c<g>r\t%1,%2\;jg%D0\t%l3"; +} + [(set_attr "op_type" "RIE") + (set_attr "type" "branch") + (set_attr "z10prop" "z10_super_c,z10_super") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 3))) (const_int 60000)) + (const_int 6) (const_int 12)))]) ; 8 byte for cr/jg + ; 10 byte for cgr/jg + +; clij, clgij, clrj, clgrj, clfi, clgfi, clr, clgr +; The following instructions do a complementary access of their second +; operand (z10 only): clrj, clgrj, clr, clgr +(define_insn "*icmp_and_br_unsigned_<mode>" + [(set (pc) + (if_then_else (match_operator 0 "s390_unsigned_integer_comparison" + [(match_operand:GPR 1 "register_operand" "d,d") + (match_operand:GPR 2 "nonmemory_operand" "d,I")]) + (pc) + (label_ref (match_operand 3 "" "")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_Z10 && !TARGET_AVOID_CMP_AND_BRANCH" +{ + if (get_attr_length (insn) == 6) + return which_alternative ? + "cl<g>ij%D0\t%1,%b2,%l3" : "cl<g>rj%D0\t%1,%2,%l3"; + else + return which_alternative ? + "cl<g>fi\t%1,%b2\;jg%D0\t%l3" : "cl<g>r\t%1,%2\;jg%D0\t%l3"; +} + [(set_attr "op_type" "RIE") + (set_attr "type" "branch") + (set_attr "z10prop" "z10_super_c,z10_super") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 3))) (const_int 60000)) + (const_int 6) (const_int 12)))]) ; 8 byte for clr/jg + ; 10 byte for clgr/jg + +;; +;;- Move instructions. +;; + +; +; movti instruction pattern(s). +; + +(define_insn "movti" + [(set (match_operand:TI 0 "nonimmediate_operand" "=d,QS,d,o") + (match_operand:TI 1 "general_operand" "QS,d,dPRT,d"))] + "TARGET_ZARCH" + "@ + lmg\t%0,%N0,%S1 + stmg\t%1,%N1,%S0 + # + #" + [(set_attr "op_type" "RSY,RSY,*,*") + (set_attr "type" "lm,stm,*,*")]) + +(define_split + [(set (match_operand:TI 0 "nonimmediate_operand" "") + (match_operand:TI 1 "general_operand" ""))] + "TARGET_ZARCH && reload_completed + && s390_split_ok_p (operands[0], operands[1], TImode, 0)" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = operand_subword (operands[0], 0, 0, TImode); + operands[3] = operand_subword (operands[0], 1, 0, TImode); + operands[4] = operand_subword (operands[1], 0, 0, TImode); + operands[5] = operand_subword (operands[1], 1, 0, TImode); +}) + +(define_split + [(set (match_operand:TI 0 "nonimmediate_operand" "") + (match_operand:TI 1 "general_operand" ""))] + "TARGET_ZARCH && reload_completed + && s390_split_ok_p (operands[0], operands[1], TImode, 1)" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = operand_subword (operands[0], 1, 0, TImode); + operands[3] = operand_subword (operands[0], 0, 0, TImode); + operands[4] = operand_subword (operands[1], 1, 0, TImode); + operands[5] = operand_subword (operands[1], 0, 0, TImode); +}) + +(define_split + [(set (match_operand:TI 0 "register_operand" "") + (match_operand:TI 1 "memory_operand" ""))] + "TARGET_ZARCH && reload_completed + && !s_operand (operands[1], VOIDmode)" + [(set (match_dup 0) (match_dup 1))] +{ + rtx addr = operand_subword (operands[0], 1, 0, TImode); + addr = gen_lowpart (Pmode, addr); + s390_load_address (addr, XEXP (operands[1], 0)); + operands[1] = replace_equiv_address (operands[1], addr); +}) + + +; +; Patterns used for secondary reloads +; + +; z10 provides move instructions accepting larl memory operands. +; Unfortunately there is no such variant for QI, TI and FP mode moves. +; These patterns are also used for unaligned SI and DI accesses. + +(define_expand "reload<INTALL:mode><P:mode>_tomem_z10" + [(parallel [(match_operand:INTALL 0 "memory_operand" "") + (match_operand:INTALL 1 "register_operand" "=d") + (match_operand:P 2 "register_operand" "=&a")])] + "TARGET_Z10" +{ + s390_reload_symref_address (operands[1], operands[0], operands[2], 1); + DONE; +}) + +(define_expand "reload<INTALL:mode><P:mode>_toreg_z10" + [(parallel [(match_operand:INTALL 0 "register_operand" "=d") + (match_operand:INTALL 1 "memory_operand" "") + (match_operand:P 2 "register_operand" "=a")])] + "TARGET_Z10" +{ + s390_reload_symref_address (operands[0], operands[1], operands[2], 0); + DONE; +}) + +(define_expand "reload<FPALL:mode><P:mode>_tomem_z10" + [(parallel [(match_operand:FPALL 0 "memory_operand" "") + (match_operand:FPALL 1 "register_operand" "=d") + (match_operand:P 2 "register_operand" "=&a")])] + "TARGET_Z10" +{ + s390_reload_symref_address (operands[1], operands[0], operands[2], 1); + DONE; +}) + +(define_expand "reload<FPALL:mode><P:mode>_toreg_z10" + [(parallel [(match_operand:FPALL 0 "register_operand" "=d") + (match_operand:FPALL 1 "memory_operand" "") + (match_operand:P 2 "register_operand" "=a")])] + "TARGET_Z10" +{ + s390_reload_symref_address (operands[0], operands[1], operands[2], 0); + DONE; +}) + +(define_expand "reload<P:mode>_larl_odd_addend_z10" + [(parallel [(match_operand:P 0 "register_operand" "=d") + (match_operand:P 1 "larl_operand" "") + (match_operand:P 2 "register_operand" "=a")])] + "TARGET_Z10" +{ + s390_reload_larl_operand (operands[0], operands[1], operands[2]); + DONE; +}) + +; Handles loading a PLUS (load address) expression + +(define_expand "reload<mode>_plus" + [(parallel [(match_operand:P 0 "register_operand" "=a") + (match_operand:P 1 "s390_plus_operand" "") + (match_operand:P 2 "register_operand" "=&a")])] + "" +{ + s390_expand_plus_operand (operands[0], operands[1], operands[2]); + DONE; +}) + +; Handles assessing a non-offsetable memory address + +(define_expand "reload<mode>_nonoffmem_in" + [(parallel [(match_operand 0 "register_operand" "") + (match_operand 1 "" "") + (match_operand:P 2 "register_operand" "=&a")])] + "" +{ + gcc_assert (MEM_P (operands[1])); + s390_load_address (operands[2], find_replacement (&XEXP (operands[1], 0))); + operands[1] = replace_equiv_address (operands[1], operands[2]); + emit_move_insn (operands[0], operands[1]); + DONE; +}) + +(define_expand "reload<mode>_nonoffmem_out" + [(parallel [(match_operand 0 "" "") + (match_operand 1 "register_operand" "") + (match_operand:P 2 "register_operand" "=&a")])] + "" +{ + gcc_assert (MEM_P (operands[0])); + s390_load_address (operands[2], find_replacement (&XEXP (operands[0], 0))); + operands[0] = replace_equiv_address (operands[0], operands[2]); + emit_move_insn (operands[0], operands[1]); + DONE; +}) + +(define_expand "reload<mode>_PIC_addr" + [(parallel [(match_operand 0 "register_operand" "=d") + (match_operand 1 "larl_operand" "") + (match_operand:P 2 "register_operand" "=a")])] + "" +{ + rtx new_rtx = legitimize_pic_address (operands[1], operands[2]); + emit_move_insn (operands[0], new_rtx); +}) + +; +; movdi instruction pattern(s). +; + +(define_expand "movdi" + [(set (match_operand:DI 0 "general_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "" +{ + /* Handle symbolic constants. */ + if (TARGET_64BIT + && (SYMBOLIC_CONST (operands[1]) + || (GET_CODE (operands[1]) == PLUS + && XEXP (operands[1], 0) == pic_offset_table_rtx + && SYMBOLIC_CONST (XEXP (operands[1], 1))))) + emit_symbolic_move (operands); +}) + +(define_insn "*movdi_larl" + [(set (match_operand:DI 0 "register_operand" "=d") + (match_operand:DI 1 "larl_operand" "X"))] + "TARGET_64BIT + && !FP_REG_P (operands[0])" + "larl\t%0,%1" + [(set_attr "op_type" "RIL") + (set_attr "type" "larl") + (set_attr "z10prop" "z10_super_A1")]) + +(define_insn "*movdi_64" + [(set (match_operand:DI 0 "nonimmediate_operand" + "=d,d,d,d,d,d,d,d,f,d,d,d,d,d, + RT,!*f,!*f,!*f,!R,!T,b,Q,d,t,Q,t") + (match_operand:DI 1 "general_operand" + "K,N0HD0,N1HD0,N2HD0,N3HD0,Os,N0SD0,N1SD0,d,f,L,b,d,RT, + d,*f,R,T,*f,*f,d,K,t,d,t,Q"))] + "TARGET_ZARCH" + "@ + lghi\t%0,%h1 + llihh\t%0,%i1 + llihl\t%0,%i1 + llilh\t%0,%i1 + llill\t%0,%i1 + lgfi\t%0,%1 + llihf\t%0,%k1 + llilf\t%0,%k1 + ldgr\t%0,%1 + lgdr\t%0,%1 + lay\t%0,%a1 + lgrl\t%0,%1 + lgr\t%0,%1 + lg\t%0,%1 + stg\t%1,%0 + ldr\t%0,%1 + ld\t%0,%1 + ldy\t%0,%1 + std\t%1,%0 + stdy\t%1,%0 + stgrl\t%1,%0 + mvghi\t%0,%1 + # + # + stam\t%1,%N1,%S0 + lam\t%0,%N0,%S1" + [(set_attr "op_type" "RI,RI,RI,RI,RI,RIL,RIL,RIL,RRE,RRE,RXY,RIL,RRE,RXY, + RXY,RR,RX,RXY,RX,RXY,RIL,SIL,*,*,RS,RS") + (set_attr "type" "*,*,*,*,*,*,*,*,floaddf,floaddf,la,larl,lr,load,store, + floaddf,floaddf,floaddf,fstoredf,fstoredf,larl,*,*,*, + *,*") + (set_attr "cpu_facility" "*,*,*,*,*,extimm,extimm,extimm,dfp,dfp,longdisp, + z10,*,*,*,*,*,longdisp,*,longdisp, + z10,z10,*,*,*,*") + (set_attr "z10prop" "z10_fwd_A1, + z10_fwd_E1, + z10_fwd_E1, + z10_fwd_E1, + z10_fwd_E1, + z10_fwd_A1, + z10_fwd_E1, + z10_fwd_E1, + *, + *, + z10_fwd_A1, + z10_fwd_A3, + z10_fr_E1, + z10_fwd_A3, + z10_rec, + *, + *, + *, + *, + *, + z10_rec, + z10_super, + *, + *, + *, + *") +]) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "register_operand" ""))] + "TARGET_ZARCH && ACCESS_REG_P (operands[1])" + [(set (match_dup 2) (match_dup 3)) + (set (match_dup 0) (ashift:DI (match_dup 0) (const_int 32))) + (set (strict_low_part (match_dup 2)) (match_dup 4))] + "operands[2] = gen_lowpart (SImode, operands[0]); + s390_split_access_reg (operands[1], &operands[4], &operands[3]);") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "register_operand" ""))] + "TARGET_ZARCH && ACCESS_REG_P (operands[0]) + && dead_or_set_p (insn, operands[1])" + [(set (match_dup 3) (match_dup 2)) + (set (match_dup 1) (lshiftrt:DI (match_dup 1) (const_int 32))) + (set (match_dup 4) (match_dup 2))] + "operands[2] = gen_lowpart (SImode, operands[1]); + s390_split_access_reg (operands[0], &operands[3], &operands[4]);") + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "register_operand" ""))] + "TARGET_ZARCH && ACCESS_REG_P (operands[0]) + && !dead_or_set_p (insn, operands[1])" + [(set (match_dup 3) (match_dup 2)) + (set (match_dup 1) (rotate:DI (match_dup 1) (const_int 32))) + (set (match_dup 4) (match_dup 2)) + (set (match_dup 1) (rotate:DI (match_dup 1) (const_int 32)))] + "operands[2] = gen_lowpart (SImode, operands[1]); + s390_split_access_reg (operands[0], &operands[3], &operands[4]);") + +(define_insn "*movdi_31" + [(set (match_operand:DI 0 "nonimmediate_operand" + "=d,d,Q,S,d ,o,!*f,!*f,!*f,!R,!T,d") + (match_operand:DI 1 "general_operand" + " Q,S,d,d,dPRT,d, *f, R, T,*f,*f,b"))] + "!TARGET_ZARCH" + "@ + lm\t%0,%N0,%S1 + lmy\t%0,%N0,%S1 + stm\t%1,%N1,%S0 + stmy\t%1,%N1,%S0 + # + # + ldr\t%0,%1 + ld\t%0,%1 + ldy\t%0,%1 + std\t%1,%0 + stdy\t%1,%0 + #" + [(set_attr "op_type" "RS,RSY,RS,RSY,*,*,RR,RX,RXY,RX,RXY,*") + (set_attr "type" "lm,lm,stm,stm,*,*,floaddf,floaddf,floaddf,fstoredf,fstoredf,*") + (set_attr "cpu_facility" "*,*,*,*,*,*,*,*,*,*,*,z10")]) + +; For a load from a symbol ref we can use one of the target registers +; together with larl to load the address. +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "memory_operand" ""))] + "!TARGET_ZARCH && reload_completed && TARGET_Z10 + && larl_operand (XEXP (operands[1], 0), SImode)" + [(set (match_dup 2) (match_dup 3)) + (set (match_dup 0) (match_dup 1))] +{ + operands[2] = operand_subword (operands[0], 1, 0, DImode); + operands[3] = XEXP (operands[1], 0); + operands[1] = replace_equiv_address (operands[1], operands[2]); +}) + +(define_split + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "!TARGET_ZARCH && reload_completed + && s390_split_ok_p (operands[0], operands[1], DImode, 0)" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = operand_subword (operands[0], 0, 0, DImode); + operands[3] = operand_subword (operands[0], 1, 0, DImode); + operands[4] = operand_subword (operands[1], 0, 0, DImode); + operands[5] = operand_subword (operands[1], 1, 0, DImode); +}) + +(define_split + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (match_operand:DI 1 "general_operand" ""))] + "!TARGET_ZARCH && reload_completed + && s390_split_ok_p (operands[0], operands[1], DImode, 1)" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = operand_subword (operands[0], 1, 0, DImode); + operands[3] = operand_subword (operands[0], 0, 0, DImode); + operands[4] = operand_subword (operands[1], 1, 0, DImode); + operands[5] = operand_subword (operands[1], 0, 0, DImode); +}) + +(define_split + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "memory_operand" ""))] + "!TARGET_ZARCH && reload_completed + && !FP_REG_P (operands[0]) + && !s_operand (operands[1], VOIDmode)" + [(set (match_dup 0) (match_dup 1))] +{ + rtx addr = operand_subword (operands[0], 1, 0, DImode); + s390_load_address (addr, XEXP (operands[1], 0)); + operands[1] = replace_equiv_address (operands[1], addr); +}) + +(define_peephole2 + [(set (match_operand:DI 0 "register_operand" "") + (mem:DI (match_operand 1 "address_operand" "")))] + "TARGET_ZARCH + && !FP_REG_P (operands[0]) + && GET_CODE (operands[1]) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (operands[1]) + && get_pool_mode (operands[1]) == DImode + && legitimate_reload_constant_p (get_pool_constant (operands[1]))" + [(set (match_dup 0) (match_dup 2))] + "operands[2] = get_pool_constant (operands[1]);") + +(define_insn "*la_64" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (match_operand:QI 1 "address_operand" "ZQZR,ZSZT"))] + "TARGET_64BIT" + "@ + la\t%0,%a1 + lay\t%0,%a1" + [(set_attr "op_type" "RX,RXY") + (set_attr "type" "la") + (set_attr "z10prop" "z10_fwd_A1,z10_fwd_A1")]) + +(define_peephole2 + [(parallel + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:QI 1 "address_operand" "")) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_64BIT + && preferred_la_operand_p (operands[1], const0_rtx)" + [(set (match_dup 0) (match_dup 1))] + "") + +(define_peephole2 + [(set (match_operand:DI 0 "register_operand" "") + (match_operand:DI 1 "register_operand" "")) + (parallel + [(set (match_dup 0) + (plus:DI (match_dup 0) + (match_operand:DI 2 "nonmemory_operand" ""))) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_64BIT + && !reg_overlap_mentioned_p (operands[0], operands[2]) + && preferred_la_operand_p (operands[1], operands[2])" + [(set (match_dup 0) (plus:DI (match_dup 1) (match_dup 2)))] + "") + +; +; movsi instruction pattern(s). +; + +(define_expand "movsi" + [(set (match_operand:SI 0 "general_operand" "") + (match_operand:SI 1 "general_operand" ""))] + "" +{ + /* Handle symbolic constants. */ + if (!TARGET_64BIT + && (SYMBOLIC_CONST (operands[1]) + || (GET_CODE (operands[1]) == PLUS + && XEXP (operands[1], 0) == pic_offset_table_rtx + && SYMBOLIC_CONST (XEXP(operands[1], 1))))) + emit_symbolic_move (operands); +}) + +(define_insn "*movsi_larl" + [(set (match_operand:SI 0 "register_operand" "=d") + (match_operand:SI 1 "larl_operand" "X"))] + "!TARGET_64BIT && TARGET_CPU_ZARCH + && !FP_REG_P (operands[0])" + "larl\t%0,%1" + [(set_attr "op_type" "RIL") + (set_attr "type" "larl") + (set_attr "z10prop" "z10_fwd_A1")]) + +(define_insn "*movsi_zarch" + [(set (match_operand:SI 0 "nonimmediate_operand" + "=d,d,d,d,d,d,d,d,d,R,T,!*f,!*f,!*f,!R,!T,d,t,Q,b,Q,t") + (match_operand:SI 1 "general_operand" + "K,N0HS0,N1HS0,Os,L,b,d,R,T,d,d,*f,R,T,*f,*f,t,d,t,d,K,Q"))] + "TARGET_ZARCH" + "@ + lhi\t%0,%h1 + llilh\t%0,%i1 + llill\t%0,%i1 + iilf\t%0,%o1 + lay\t%0,%a1 + lrl\t%0,%1 + lr\t%0,%1 + l\t%0,%1 + ly\t%0,%1 + st\t%1,%0 + sty\t%1,%0 + ler\t%0,%1 + le\t%0,%1 + ley\t%0,%1 + ste\t%1,%0 + stey\t%1,%0 + ear\t%0,%1 + sar\t%0,%1 + stam\t%1,%1,%S0 + strl\t%1,%0 + mvhi\t%0,%1 + lam\t%0,%0,%S1" + [(set_attr "op_type" "RI,RI,RI,RIL,RXY,RIL,RR,RX,RXY,RX,RXY, + RR,RX,RXY,RX,RXY,RRE,RRE,RS,RIL,SIL,RS") + (set_attr "type" "*, + *, + *, + *, + la, + larl, + lr, + load, + load, + store, + store, + floadsf, + floadsf, + floadsf, + fstoresf, + fstoresf, + *, + *, + *, + larl, + *, + *") + (set_attr "cpu_facility" "*,*,*,extimm,longdisp,z10,*,*,longdisp,*,longdisp, + *,*,longdisp,*,longdisp,*,*,*,z10,z10,*") + (set_attr "z10prop" "z10_fwd_A1, + z10_fwd_E1, + z10_fwd_E1, + z10_fwd_A1, + z10_fwd_A1, + z10_fwd_A3, + z10_fr_E1, + z10_fwd_A3, + z10_fwd_A3, + z10_rec, + z10_rec, + *, + *, + *, + *, + *, + z10_super_E1, + z10_super, + *, + z10_rec, + z10_super, + *")]) + +(define_insn "*movsi_esa" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,R,!*f,!*f,!R,d,t,Q,t") + (match_operand:SI 1 "general_operand" "K,d,R,d,*f,R,*f,t,d,t,Q"))] + "!TARGET_ZARCH" + "@ + lhi\t%0,%h1 + lr\t%0,%1 + l\t%0,%1 + st\t%1,%0 + ler\t%0,%1 + le\t%0,%1 + ste\t%1,%0 + ear\t%0,%1 + sar\t%0,%1 + stam\t%1,%1,%S0 + lam\t%0,%0,%S1" + [(set_attr "op_type" "RI,RR,RX,RX,RR,RX,RX,RRE,RRE,RS,RS") + (set_attr "type" "*,lr,load,store,floadsf,floadsf,fstoresf,*,*,*,*") + (set_attr "z10prop" "z10_fwd_A1, + z10_fr_E1, + z10_fwd_A3, + z10_rec, + *, + *, + *, + z10_super_E1, + z10_super, + *, + *") +]) + +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (mem:SI (match_operand 1 "address_operand" "")))] + "!FP_REG_P (operands[0]) + && GET_CODE (operands[1]) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (operands[1]) + && get_pool_mode (operands[1]) == SImode + && legitimate_reload_constant_p (get_pool_constant (operands[1]))" + [(set (match_dup 0) (match_dup 2))] + "operands[2] = get_pool_constant (operands[1]);") + +(define_insn "*la_31" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (match_operand:QI 1 "address_operand" "ZQZR,ZSZT"))] + "!TARGET_64BIT && legitimate_la_operand_p (operands[1])" + "@ + la\t%0,%a1 + lay\t%0,%a1" + [(set_attr "op_type" "RX,RXY") + (set_attr "type" "la") + (set_attr "z10prop" "z10_fwd_A1,z10_fwd_A1")]) + +(define_peephole2 + [(parallel + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:QI 1 "address_operand" "")) + (clobber (reg:CC CC_REGNUM))])] + "!TARGET_64BIT + && preferred_la_operand_p (operands[1], const0_rtx)" + [(set (match_dup 0) (match_dup 1))] + "") + +(define_peephole2 + [(set (match_operand:SI 0 "register_operand" "") + (match_operand:SI 1 "register_operand" "")) + (parallel + [(set (match_dup 0) + (plus:SI (match_dup 0) + (match_operand:SI 2 "nonmemory_operand" ""))) + (clobber (reg:CC CC_REGNUM))])] + "!TARGET_64BIT + && !reg_overlap_mentioned_p (operands[0], operands[2]) + && preferred_la_operand_p (operands[1], operands[2])" + [(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))] + "") + +(define_insn "*la_31_and" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (and:SI (match_operand:QI 1 "address_operand" "ZQZR,ZSZT") + (const_int 2147483647)))] + "!TARGET_64BIT" + "@ + la\t%0,%a1 + lay\t%0,%a1" + [(set_attr "op_type" "RX,RXY") + (set_attr "type" "la") + (set_attr "z10prop" "z10_fwd_A1,z10_fwd_A1")]) + +(define_insn_and_split "*la_31_and_cc" + [(set (match_operand:SI 0 "register_operand" "=d") + (and:SI (match_operand:QI 1 "address_operand" "p") + (const_int 2147483647))) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_64BIT" + "#" + "&& reload_completed" + [(set (match_dup 0) + (and:SI (match_dup 1) (const_int 2147483647)))] + "" + [(set_attr "op_type" "RX") + (set_attr "type" "la")]) + +(define_insn "force_la_31" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (match_operand:QI 1 "address_operand" "ZQZR,ZSZT")) + (use (const_int 0))] + "!TARGET_64BIT" + "@ + la\t%0,%a1 + lay\t%0,%a1" + [(set_attr "op_type" "RX") + (set_attr "type" "la") + (set_attr "z10prop" "z10_fwd_A1,z10_fwd_A1")]) + +; +; movhi instruction pattern(s). +; + +(define_expand "movhi" + [(set (match_operand:HI 0 "nonimmediate_operand" "") + (match_operand:HI 1 "general_operand" ""))] + "" +{ + /* Make it explicit that loading a register from memory + always sign-extends (at least) to SImode. */ + if (optimize && can_create_pseudo_p () + && register_operand (operands[0], VOIDmode) + && GET_CODE (operands[1]) == MEM) + { + rtx tmp = gen_reg_rtx (SImode); + rtx ext = gen_rtx_SIGN_EXTEND (SImode, operands[1]); + emit_insn (gen_rtx_SET (VOIDmode, tmp, ext)); + operands[1] = gen_lowpart (HImode, tmp); + } +}) + +(define_insn "*movhi" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,d,d,R,T,b,Q") + (match_operand:HI 1 "general_operand" " d,n,R,T,b,d,d,d,K"))] + "" + "@ + lr\t%0,%1 + lhi\t%0,%h1 + lh\t%0,%1 + lhy\t%0,%1 + lhrl\t%0,%1 + sth\t%1,%0 + sthy\t%1,%0 + sthrl\t%1,%0 + mvhhi\t%0,%1" + [(set_attr "op_type" "RR,RI,RX,RXY,RIL,RX,RXY,RIL,SIL") + (set_attr "type" "lr,*,*,*,larl,store,store,store,*") + (set_attr "cpu_facility" "*,*,*,*,z10,*,*,z10,z10") + (set_attr "z10prop" "z10_fr_E1, + z10_fwd_A1, + z10_super_E1, + z10_super_E1, + z10_super_E1, + z10_rec, + z10_rec, + z10_rec, + z10_super")]) + +(define_peephole2 + [(set (match_operand:HI 0 "register_operand" "") + (mem:HI (match_operand 1 "address_operand" "")))] + "GET_CODE (operands[1]) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (operands[1]) + && get_pool_mode (operands[1]) == HImode + && GET_CODE (get_pool_constant (operands[1])) == CONST_INT" + [(set (match_dup 0) (match_dup 2))] + "operands[2] = get_pool_constant (operands[1]);") + +; +; movqi instruction pattern(s). +; + +(define_expand "movqi" + [(set (match_operand:QI 0 "nonimmediate_operand" "") + (match_operand:QI 1 "general_operand" ""))] + "" +{ + /* On z/Architecture, zero-extending from memory to register + is just as fast as a QImode load. */ + if (TARGET_ZARCH && optimize && can_create_pseudo_p () + && register_operand (operands[0], VOIDmode) + && GET_CODE (operands[1]) == MEM) + { + rtx tmp = gen_reg_rtx (DImode); + rtx ext = gen_rtx_ZERO_EXTEND (DImode, operands[1]); + emit_insn (gen_rtx_SET (VOIDmode, tmp, ext)); + operands[1] = gen_lowpart (QImode, tmp); + } +}) + +(define_insn "*movqi" + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,d,R,T,Q,S,?Q") + (match_operand:QI 1 "general_operand" " d,n,R,T,d,d,n,n,?Q"))] + "" + "@ + lr\t%0,%1 + lhi\t%0,%b1 + ic\t%0,%1 + icy\t%0,%1 + stc\t%1,%0 + stcy\t%1,%0 + mvi\t%S0,%b1 + mviy\t%S0,%b1 + #" + [(set_attr "op_type" "RR,RI,RX,RXY,RX,RXY,SI,SIY,SS") + (set_attr "type" "lr,*,*,*,store,store,store,store,*") + (set_attr "z10prop" "z10_fr_E1, + z10_fwd_A1, + z10_super_E1, + z10_super_E1, + z10_rec, + z10_rec, + z10_super, + z10_super, + *")]) + +(define_peephole2 + [(set (match_operand:QI 0 "nonimmediate_operand" "") + (mem:QI (match_operand 1 "address_operand" "")))] + "GET_CODE (operands[1]) == SYMBOL_REF + && CONSTANT_POOL_ADDRESS_P (operands[1]) + && get_pool_mode (operands[1]) == QImode + && GET_CODE (get_pool_constant (operands[1])) == CONST_INT" + [(set (match_dup 0) (match_dup 2))] + "operands[2] = get_pool_constant (operands[1]);") + +; +; movstrictqi instruction pattern(s). +; + +(define_insn "*movstrictqi" + [(set (strict_low_part (match_operand:QI 0 "register_operand" "+d,d")) + (match_operand:QI 1 "memory_operand" "R,T"))] + "" + "@ + ic\t%0,%1 + icy\t%0,%1" + [(set_attr "op_type" "RX,RXY") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + +; +; movstricthi instruction pattern(s). +; + +(define_insn "*movstricthi" + [(set (strict_low_part (match_operand:HI 0 "register_operand" "+d,d")) + (match_operand:HI 1 "memory_operand" "Q,S")) + (clobber (reg:CC CC_REGNUM))] + "" + "@ + icm\t%0,3,%S1 + icmy\t%0,3,%S1" + [(set_attr "op_type" "RS,RSY") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + +; +; movstrictsi instruction pattern(s). +; + +(define_insn "movstrictsi" + [(set (strict_low_part (match_operand:SI 0 "register_operand" "+d,d,d,d")) + (match_operand:SI 1 "general_operand" "d,R,T,t"))] + "TARGET_ZARCH" + "@ + lr\t%0,%1 + l\t%0,%1 + ly\t%0,%1 + ear\t%0,%1" + [(set_attr "op_type" "RR,RX,RXY,RRE") + (set_attr "type" "lr,load,load,*") + (set_attr "z10prop" "z10_fr_E1,z10_fwd_A3,z10_fwd_A3,z10_super_E1")]) + +; +; mov(tf|td) instruction pattern(s). +; + +(define_expand "mov<mode>" + [(set (match_operand:TD_TF 0 "nonimmediate_operand" "") + (match_operand:TD_TF 1 "general_operand" ""))] + "" + "") + +(define_insn "*mov<mode>_64" + [(set (match_operand:TD_TF 0 "nonimmediate_operand" "=f,f,f,o, d,QS, d,o") + (match_operand:TD_TF 1 "general_operand" " G,f,o,f,QS, d,dRT,d"))] + "TARGET_ZARCH" + "@ + lzxr\t%0 + lxr\t%0,%1 + # + # + lmg\t%0,%N0,%S1 + stmg\t%1,%N1,%S0 + # + #" + [(set_attr "op_type" "RRE,RRE,*,*,RSY,RSY,*,*") + (set_attr "type" "fsimptf,fsimptf,*,*,lm,stm,*,*") + (set_attr "cpu_facility" "z196,*,*,*,*,*,*,*")]) + +(define_insn "*mov<mode>_31" + [(set (match_operand:TD_TF 0 "nonimmediate_operand" "=f,f,f,o") + (match_operand:TD_TF 1 "general_operand" " G,f,o,f"))] + "!TARGET_ZARCH" + "@ + lzxr\t%0 + lxr\t%0,%1 + # + #" + [(set_attr "op_type" "RRE,RRE,*,*") + (set_attr "type" "fsimptf,fsimptf,*,*") + (set_attr "cpu_facility" "z196,*,*,*")]) + +; TFmode in GPRs splitters + +(define_split + [(set (match_operand:TD_TF 0 "nonimmediate_operand" "") + (match_operand:TD_TF 1 "general_operand" ""))] + "TARGET_ZARCH && reload_completed + && s390_split_ok_p (operands[0], operands[1], <MODE>mode, 0)" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = operand_subword (operands[0], 0, 0, <MODE>mode); + operands[3] = operand_subword (operands[0], 1, 0, <MODE>mode); + operands[4] = operand_subword (operands[1], 0, 0, <MODE>mode); + operands[5] = operand_subword (operands[1], 1, 0, <MODE>mode); +}) + +(define_split + [(set (match_operand:TD_TF 0 "nonimmediate_operand" "") + (match_operand:TD_TF 1 "general_operand" ""))] + "TARGET_ZARCH && reload_completed + && s390_split_ok_p (operands[0], operands[1], <MODE>mode, 1)" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = operand_subword (operands[0], 1, 0, <MODE>mode); + operands[3] = operand_subword (operands[0], 0, 0, <MODE>mode); + operands[4] = operand_subword (operands[1], 1, 0, <MODE>mode); + operands[5] = operand_subword (operands[1], 0, 0, <MODE>mode); +}) + +(define_split + [(set (match_operand:TD_TF 0 "register_operand" "") + (match_operand:TD_TF 1 "memory_operand" ""))] + "TARGET_ZARCH && reload_completed + && !FP_REG_P (operands[0]) + && !s_operand (operands[1], VOIDmode)" + [(set (match_dup 0) (match_dup 1))] +{ + rtx addr = operand_subword (operands[0], 1, 0, <MODE>mode); + addr = gen_lowpart (Pmode, addr); + s390_load_address (addr, XEXP (operands[1], 0)); + operands[1] = replace_equiv_address (operands[1], addr); +}) + +; TFmode in BFPs splitters + +(define_split + [(set (match_operand:TD_TF 0 "register_operand" "") + (match_operand:TD_TF 1 "memory_operand" ""))] + "reload_completed && offsettable_memref_p (operands[1]) + && FP_REG_P (operands[0])" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = simplify_gen_subreg (<HALF_TMODE>mode, operands[0], + <MODE>mode, 0); + operands[3] = simplify_gen_subreg (<HALF_TMODE>mode, operands[0], + <MODE>mode, 8); + operands[4] = adjust_address_nv (operands[1], <HALF_TMODE>mode, 0); + operands[5] = adjust_address_nv (operands[1], <HALF_TMODE>mode, 8); +}) + +(define_split + [(set (match_operand:TD_TF 0 "memory_operand" "") + (match_operand:TD_TF 1 "register_operand" ""))] + "reload_completed && offsettable_memref_p (operands[0]) + && FP_REG_P (operands[1])" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = adjust_address_nv (operands[0], <HALF_TMODE>mode, 0); + operands[3] = adjust_address_nv (operands[0], <HALF_TMODE>mode, 8); + operands[4] = simplify_gen_subreg (<HALF_TMODE>mode, operands[1], + <MODE>mode, 0); + operands[5] = simplify_gen_subreg (<HALF_TMODE>mode, operands[1], + <MODE>mode, 8); +}) + +; +; mov(df|dd) instruction pattern(s). +; + +(define_expand "mov<mode>" + [(set (match_operand:DD_DF 0 "nonimmediate_operand" "") + (match_operand:DD_DF 1 "general_operand" ""))] + "" + "") + +(define_insn "*mov<mode>_64dfp" + [(set (match_operand:DD_DF 0 "nonimmediate_operand" + "=f,f,f,d,f,f,R,T,d,d, d,RT") + (match_operand:DD_DF 1 "general_operand" + " G,f,d,f,R,T,f,f,G,d,RT, d"))] + "TARGET_DFP" + "@ + lzdr\t%0 + ldr\t%0,%1 + ldgr\t%0,%1 + lgdr\t%0,%1 + ld\t%0,%1 + ldy\t%0,%1 + std\t%1,%0 + stdy\t%1,%0 + lghi\t%0,0 + lgr\t%0,%1 + lg\t%0,%1 + stg\t%1,%0" + [(set_attr "op_type" "RRE,RR,RRE,RRE,RX,RXY,RX,RXY,RI,RRE,RXY,RXY") + (set_attr "type" "fsimpdf,floaddf,floaddf,floaddf,floaddf,floaddf, + fstoredf,fstoredf,*,lr,load,store") + (set_attr "z10prop" "*,*,*,*,*,*,*,*,z10_fwd_A1,z10_fr_E1,z10_fwd_A3,z10_rec") + (set_attr "cpu_facility" "z196,*,*,*,*,*,*,*,*,*,*,*")]) + +(define_insn "*mov<mode>_64" + [(set (match_operand:DD_DF 0 "nonimmediate_operand" "=f,f,f,f,R,T,d,d, d,RT") + (match_operand:DD_DF 1 "general_operand" " G,f,R,T,f,f,G,d,RT, d"))] + "TARGET_ZARCH" + "@ + lzdr\t%0 + ldr\t%0,%1 + ld\t%0,%1 + ldy\t%0,%1 + std\t%1,%0 + stdy\t%1,%0 + lghi\t%0,0 + lgr\t%0,%1 + lg\t%0,%1 + stg\t%1,%0" + [(set_attr "op_type" "RRE,RR,RX,RXY,RX,RXY,RI,RRE,RXY,RXY") + (set_attr "type" "fsimpdf,fload<mode>,fload<mode>,fload<mode>, + fstore<mode>,fstore<mode>,*,lr,load,store") + (set_attr "z10prop" "*,*,*,*,*,*,z10_fwd_A1,z10_fr_E1,z10_fwd_A3,z10_rec") + (set_attr "cpu_facility" "z196,*,*,*,*,*,*,*,*,*")]) + +(define_insn "*mov<mode>_31" + [(set (match_operand:DD_DF 0 "nonimmediate_operand" + "=f,f,f,f,R,T,d,d,Q,S, d,o") + (match_operand:DD_DF 1 "general_operand" + " G,f,R,T,f,f,Q,S,d,d,dPRT,d"))] + "!TARGET_ZARCH" + "@ + lzdr\t%0 + ldr\t%0,%1 + ld\t%0,%1 + ldy\t%0,%1 + std\t%1,%0 + stdy\t%1,%0 + lm\t%0,%N0,%S1 + lmy\t%0,%N0,%S1 + stm\t%1,%N1,%S0 + stmy\t%1,%N1,%S0 + # + #" + [(set_attr "op_type" "RRE,RR,RX,RXY,RX,RXY,RS,RSY,RS,RSY,*,*") + (set_attr "type" "fsimpdf,fload<mode>,fload<mode>,fload<mode>, + fstore<mode>,fstore<mode>,lm,lm,stm,stm,*,*") + (set_attr "cpu_facility" "z196,*,*,*,*,*,*,*,*,*,*,*")]) + +(define_split + [(set (match_operand:DD_DF 0 "nonimmediate_operand" "") + (match_operand:DD_DF 1 "general_operand" ""))] + "!TARGET_ZARCH && reload_completed + && s390_split_ok_p (operands[0], operands[1], <MODE>mode, 0)" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = operand_subword (operands[0], 0, 0, <MODE>mode); + operands[3] = operand_subword (operands[0], 1, 0, <MODE>mode); + operands[4] = operand_subword (operands[1], 0, 0, <MODE>mode); + operands[5] = operand_subword (operands[1], 1, 0, <MODE>mode); +}) + +(define_split + [(set (match_operand:DD_DF 0 "nonimmediate_operand" "") + (match_operand:DD_DF 1 "general_operand" ""))] + "!TARGET_ZARCH && reload_completed + && s390_split_ok_p (operands[0], operands[1], <MODE>mode, 1)" + [(set (match_dup 2) (match_dup 4)) + (set (match_dup 3) (match_dup 5))] +{ + operands[2] = operand_subword (operands[0], 1, 0, <MODE>mode); + operands[3] = operand_subword (operands[0], 0, 0, <MODE>mode); + operands[4] = operand_subword (operands[1], 1, 0, <MODE>mode); + operands[5] = operand_subword (operands[1], 0, 0, <MODE>mode); +}) + +(define_split + [(set (match_operand:DD_DF 0 "register_operand" "") + (match_operand:DD_DF 1 "memory_operand" ""))] + "!TARGET_ZARCH && reload_completed + && !FP_REG_P (operands[0]) + && !s_operand (operands[1], VOIDmode)" + [(set (match_dup 0) (match_dup 1))] +{ + rtx addr = operand_subword (operands[0], 1, 0, <MODE>mode); + s390_load_address (addr, XEXP (operands[1], 0)); + operands[1] = replace_equiv_address (operands[1], addr); +}) + +; +; mov(sf|sd) instruction pattern(s). +; + +(define_insn "mov<mode>" + [(set (match_operand:SD_SF 0 "nonimmediate_operand" + "=f,f,f,f,R,T,d,d,d,d,R,T") + (match_operand:SD_SF 1 "general_operand" + " G,f,R,T,f,f,G,d,R,T,d,d"))] + "" + "@ + lzer\t%0 + ler\t%0,%1 + le\t%0,%1 + ley\t%0,%1 + ste\t%1,%0 + stey\t%1,%0 + lhi\t%0,0 + lr\t%0,%1 + l\t%0,%1 + ly\t%0,%1 + st\t%1,%0 + sty\t%1,%0" + [(set_attr "op_type" "RRE,RR,RX,RXY,RX,RXY,RI,RR,RX,RXY,RX,RXY") + (set_attr "type" "fsimpsf,fload<mode>,fload<mode>,fload<mode>, + fstore<mode>,fstore<mode>,*,lr,load,load,store,store") + (set_attr "z10prop" "*,*,*,*,*,*,z10_fwd_A1,z10_fr_E1,z10_fwd_A3,z10_fwd_A3,z10_rec,z10_rec") + (set_attr "cpu_facility" "z196,*,*,*,*,*,*,*,*,*,*,*")]) + +; +; movcc instruction pattern +; + +(define_insn "movcc" + [(set (match_operand:CC 0 "nonimmediate_operand" "=d,c,d,d,d,R,T") + (match_operand:CC 1 "nonimmediate_operand" "d,d,c,R,T,d,d"))] + "" + "@ + lr\t%0,%1 + tmh\t%1,12288 + ipm\t%0 + st\t%0,%1 + sty\t%0,%1 + l\t%1,%0 + ly\t%1,%0" + [(set_attr "op_type" "RR,RI,RRE,RX,RXY,RX,RXY") + (set_attr "type" "lr,*,*,store,store,load,load") + (set_attr "z10prop" "z10_fr_E1,z10_super,*,z10_rec,z10_rec,z10_fwd_A3,z10_fwd_A3") + (set_attr "z196prop" "*,*,z196_ends,*,*,*,*")]) + +; +; Block move (MVC) patterns. +; + +(define_insn "*mvc" + [(set (match_operand:BLK 0 "memory_operand" "=Q") + (match_operand:BLK 1 "memory_operand" "Q")) + (use (match_operand 2 "const_int_operand" "n"))] + "INTVAL (operands[2]) >= 1 && INTVAL (operands[2]) <= 256" + "mvc\t%O0(%2,%R0),%S1" + [(set_attr "op_type" "SS")]) + +; This splitter converts a QI to QI mode copy into a BLK mode copy in +; order to have it implemented with mvc. + +(define_split + [(set (match_operand:QI 0 "memory_operand" "") + (match_operand:QI 1 "memory_operand" ""))] + "reload_completed" + [(parallel + [(set (match_dup 0) (match_dup 1)) + (use (const_int 1))])] +{ + operands[0] = adjust_address (operands[0], BLKmode, 0); + operands[1] = adjust_address (operands[1], BLKmode, 0); +}) + + +(define_peephole2 + [(parallel + [(set (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" "")) + (use (match_operand 2 "const_int_operand" ""))]) + (parallel + [(set (match_operand:BLK 3 "memory_operand" "") + (match_operand:BLK 4 "memory_operand" "")) + (use (match_operand 5 "const_int_operand" ""))])] + "s390_offset_p (operands[0], operands[3], operands[2]) + && s390_offset_p (operands[1], operands[4], operands[2]) + && !s390_overlap_p (operands[0], operands[1], + INTVAL (operands[2]) + INTVAL (operands[5])) + && INTVAL (operands[2]) + INTVAL (operands[5]) <= 256" + [(parallel + [(set (match_dup 6) (match_dup 7)) + (use (match_dup 8))])] + "operands[6] = gen_rtx_MEM (BLKmode, XEXP (operands[0], 0)); + operands[7] = gen_rtx_MEM (BLKmode, XEXP (operands[1], 0)); + operands[8] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[5]));") + + +; +; load_multiple pattern(s). +; +; ??? Due to reload problems with replacing registers inside match_parallel +; we currently support load_multiple/store_multiple only after reload. +; + +(define_expand "load_multiple" + [(match_par_dup 3 [(set (match_operand 0 "" "") + (match_operand 1 "" "")) + (use (match_operand 2 "" ""))])] + "reload_completed" +{ + enum machine_mode mode; + int regno; + int count; + rtx from; + int i, off; + + /* Support only loading a constant number of fixed-point registers from + memory and only bother with this if more than two */ + if (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) < 2 + || INTVAL (operands[2]) > 16 + || GET_CODE (operands[1]) != MEM + || GET_CODE (operands[0]) != REG + || REGNO (operands[0]) >= 16) + FAIL; + + count = INTVAL (operands[2]); + regno = REGNO (operands[0]); + mode = GET_MODE (operands[0]); + if (mode != SImode && (!TARGET_ZARCH || mode != DImode)) + FAIL; + + operands[3] = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); + if (!can_create_pseudo_p ()) + { + if (GET_CODE (XEXP (operands[1], 0)) == REG) + { + from = XEXP (operands[1], 0); + off = 0; + } + else if (GET_CODE (XEXP (operands[1], 0)) == PLUS + && GET_CODE (XEXP (XEXP (operands[1], 0), 0)) == REG + && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == CONST_INT) + { + from = XEXP (XEXP (operands[1], 0), 0); + off = INTVAL (XEXP (XEXP (operands[1], 0), 1)); + } + else + FAIL; + } + else + { + from = force_reg (Pmode, XEXP (operands[1], 0)); + off = 0; + } + + for (i = 0; i < count; i++) + XVECEXP (operands[3], 0, i) + = gen_rtx_SET (VOIDmode, gen_rtx_REG (mode, regno + i), + change_address (operands[1], mode, + plus_constant (from, off + i * GET_MODE_SIZE (mode)))); +}) + +(define_insn "*load_multiple_di" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:DI 1 "register_operand" "=r") + (match_operand:DI 2 "s_operand" "QS"))])] + "reload_completed && TARGET_ZARCH" +{ + int words = XVECLEN (operands[0], 0); + operands[0] = gen_rtx_REG (DImode, REGNO (operands[1]) + words - 1); + return "lmg\t%1,%0,%S2"; +} + [(set_attr "op_type" "RSY") + (set_attr "type" "lm")]) + +(define_insn "*load_multiple_si" + [(match_parallel 0 "load_multiple_operation" + [(set (match_operand:SI 1 "register_operand" "=r,r") + (match_operand:SI 2 "s_operand" "Q,S"))])] + "reload_completed" +{ + int words = XVECLEN (operands[0], 0); + operands[0] = gen_rtx_REG (SImode, REGNO (operands[1]) + words - 1); + return which_alternative == 0 ? "lm\t%1,%0,%S2" : "lmy\t%1,%0,%S2"; +} + [(set_attr "op_type" "RS,RSY") + (set_attr "type" "lm")]) + +; +; store multiple pattern(s). +; + +(define_expand "store_multiple" + [(match_par_dup 3 [(set (match_operand 0 "" "") + (match_operand 1 "" "")) + (use (match_operand 2 "" ""))])] + "reload_completed" +{ + enum machine_mode mode; + int regno; + int count; + rtx to; + int i, off; + + /* Support only storing a constant number of fixed-point registers to + memory and only bother with this if more than two. */ + if (GET_CODE (operands[2]) != CONST_INT + || INTVAL (operands[2]) < 2 + || INTVAL (operands[2]) > 16 + || GET_CODE (operands[0]) != MEM + || GET_CODE (operands[1]) != REG + || REGNO (operands[1]) >= 16) + FAIL; + + count = INTVAL (operands[2]); + regno = REGNO (operands[1]); + mode = GET_MODE (operands[1]); + if (mode != SImode && (!TARGET_ZARCH || mode != DImode)) + FAIL; + + operands[3] = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count)); + + if (!can_create_pseudo_p ()) + { + if (GET_CODE (XEXP (operands[0], 0)) == REG) + { + to = XEXP (operands[0], 0); + off = 0; + } + else if (GET_CODE (XEXP (operands[0], 0)) == PLUS + && GET_CODE (XEXP (XEXP (operands[0], 0), 0)) == REG + && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == CONST_INT) + { + to = XEXP (XEXP (operands[0], 0), 0); + off = INTVAL (XEXP (XEXP (operands[0], 0), 1)); + } + else + FAIL; + } + else + { + to = force_reg (Pmode, XEXP (operands[0], 0)); + off = 0; + } + + for (i = 0; i < count; i++) + XVECEXP (operands[3], 0, i) + = gen_rtx_SET (VOIDmode, + change_address (operands[0], mode, + plus_constant (to, off + i * GET_MODE_SIZE (mode))), + gen_rtx_REG (mode, regno + i)); +}) + +(define_insn "*store_multiple_di" + [(match_parallel 0 "store_multiple_operation" + [(set (match_operand:DI 1 "s_operand" "=QS") + (match_operand:DI 2 "register_operand" "r"))])] + "reload_completed && TARGET_ZARCH" +{ + int words = XVECLEN (operands[0], 0); + operands[0] = gen_rtx_REG (DImode, REGNO (operands[2]) + words - 1); + return "stmg\t%2,%0,%S1"; +} + [(set_attr "op_type" "RSY") + (set_attr "type" "stm")]) + + +(define_insn "*store_multiple_si" + [(match_parallel 0 "store_multiple_operation" + [(set (match_operand:SI 1 "s_operand" "=Q,S") + (match_operand:SI 2 "register_operand" "r,r"))])] + "reload_completed" +{ + int words = XVECLEN (operands[0], 0); + operands[0] = gen_rtx_REG (SImode, REGNO (operands[2]) + words - 1); + return which_alternative == 0 ? "stm\t%2,%0,%S1" : "stmy\t%2,%0,%S1"; +} + [(set_attr "op_type" "RS,RSY") + (set_attr "type" "stm")]) + +;; +;; String instructions. +;; + +(define_insn "*execute_rl" + [(match_parallel 0 "" + [(unspec [(match_operand 1 "register_operand" "a") + (match_operand 2 "" "") + (match_operand:SI 3 "larl_operand" "X")] UNSPEC_EXECUTE)])] + "TARGET_Z10 && GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT + && GET_MODE_SIZE (GET_MODE (operands[1])) <= UNITS_PER_WORD" + "exrl\t%1,%3" + [(set_attr "op_type" "RIL") + (set_attr "type" "cs")]) + +(define_insn "*execute" + [(match_parallel 0 "" + [(unspec [(match_operand 1 "register_operand" "a") + (match_operand:BLK 2 "memory_operand" "R") + (match_operand 3 "" "")] UNSPEC_EXECUTE)])] + "GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_INT + && GET_MODE_SIZE (GET_MODE (operands[1])) <= UNITS_PER_WORD" + "ex\t%1,%2" + [(set_attr "op_type" "RX") + (set_attr "type" "cs")]) + + +; +; strlenM instruction pattern(s). +; + +(define_expand "strlen<mode>" + [(set (reg:SI 0) (match_operand:SI 2 "immediate_operand" "")) + (parallel + [(set (match_dup 4) + (unspec:P [(const_int 0) + (match_operand:BLK 1 "memory_operand" "") + (reg:SI 0) + (match_operand 3 "immediate_operand" "")] UNSPEC_SRST)) + (clobber (scratch:P)) + (clobber (reg:CC CC_REGNUM))]) + (parallel + [(set (match_operand:P 0 "register_operand" "") + (minus:P (match_dup 4) (match_dup 5))) + (clobber (reg:CC CC_REGNUM))])] + "" +{ + operands[4] = gen_reg_rtx (Pmode); + operands[5] = gen_reg_rtx (Pmode); + emit_move_insn (operands[5], force_operand (XEXP (operands[1], 0), NULL_RTX)); + operands[1] = replace_equiv_address (operands[1], operands[5]); +}) + +(define_insn "*strlen<mode>" + [(set (match_operand:P 0 "register_operand" "=a") + (unspec:P [(match_operand:P 2 "general_operand" "0") + (mem:BLK (match_operand:P 3 "register_operand" "1")) + (reg:SI 0) + (match_operand 4 "immediate_operand" "")] UNSPEC_SRST)) + (clobber (match_scratch:P 1 "=a")) + (clobber (reg:CC CC_REGNUM))] + "" + "srst\t%0,%1\;jo\t.-4" + [(set_attr "length" "8") + (set_attr "type" "vs")]) + +; +; cmpstrM instruction pattern(s). +; + +(define_expand "cmpstrsi" + [(set (reg:SI 0) (const_int 0)) + (parallel + [(clobber (match_operand 3 "" "")) + (clobber (match_dup 4)) + (set (reg:CCU CC_REGNUM) + (compare:CCU (match_operand:BLK 1 "memory_operand" "") + (match_operand:BLK 2 "memory_operand" ""))) + (use (reg:SI 0))]) + (parallel + [(set (match_operand:SI 0 "register_operand" "=d") + (unspec:SI [(reg:CCU CC_REGNUM)] UNSPEC_CCU_TO_INT)) + (clobber (reg:CC CC_REGNUM))])] + "" +{ + /* As the result of CMPINT is inverted compared to what we need, + we have to swap the operands. */ + rtx op1 = operands[2]; + rtx op2 = operands[1]; + rtx addr1 = gen_reg_rtx (Pmode); + rtx addr2 = gen_reg_rtx (Pmode); + + emit_move_insn (addr1, force_operand (XEXP (op1, 0), NULL_RTX)); + emit_move_insn (addr2, force_operand (XEXP (op2, 0), NULL_RTX)); + operands[1] = replace_equiv_address_nv (op1, addr1); + operands[2] = replace_equiv_address_nv (op2, addr2); + operands[3] = addr1; + operands[4] = addr2; +}) + +(define_insn "*cmpstr<mode>" + [(clobber (match_operand:P 0 "register_operand" "=d")) + (clobber (match_operand:P 1 "register_operand" "=d")) + (set (reg:CCU CC_REGNUM) + (compare:CCU (mem:BLK (match_operand:P 2 "register_operand" "0")) + (mem:BLK (match_operand:P 3 "register_operand" "1")))) + (use (reg:SI 0))] + "" + "clst\t%0,%1\;jo\t.-4" + [(set_attr "length" "8") + (set_attr "type" "vs")]) + +; +; movstr instruction pattern. +; + +(define_expand "movstr" + [(set (reg:SI 0) (const_int 0)) + (parallel + [(clobber (match_dup 3)) + (set (match_operand:BLK 1 "memory_operand" "") + (match_operand:BLK 2 "memory_operand" "")) + (set (match_operand 0 "register_operand" "") + (unspec [(match_dup 1) + (match_dup 2) + (reg:SI 0)] UNSPEC_MVST)) + (clobber (reg:CC CC_REGNUM))])] + "" +{ + rtx addr1 = gen_reg_rtx (Pmode); + rtx addr2 = gen_reg_rtx (Pmode); + + emit_move_insn (addr1, force_operand (XEXP (operands[1], 0), NULL_RTX)); + emit_move_insn (addr2, force_operand (XEXP (operands[2], 0), NULL_RTX)); + operands[1] = replace_equiv_address_nv (operands[1], addr1); + operands[2] = replace_equiv_address_nv (operands[2], addr2); + operands[3] = addr2; +}) + +(define_insn "*movstr" + [(clobber (match_operand:P 2 "register_operand" "=d")) + (set (mem:BLK (match_operand:P 1 "register_operand" "0")) + (mem:BLK (match_operand:P 3 "register_operand" "2"))) + (set (match_operand:P 0 "register_operand" "=d") + (unspec [(mem:BLK (match_dup 1)) + (mem:BLK (match_dup 3)) + (reg:SI 0)] UNSPEC_MVST)) + (clobber (reg:CC CC_REGNUM))] + "" + "mvst\t%1,%2\;jo\t.-4" + [(set_attr "length" "8") + (set_attr "type" "vs")]) + + +; +; movmemM instruction pattern(s). +; + +(define_expand "movmem<mode>" + [(set (match_operand:BLK 0 "memory_operand" "") ; destination + (match_operand:BLK 1 "memory_operand" "")) ; source + (use (match_operand:GPR 2 "general_operand" "")) ; count + (match_operand 3 "" "")] + "" + "s390_expand_movmem (operands[0], operands[1], operands[2]); DONE;") + +; Move a block that is up to 256 bytes in length. +; The block length is taken as (operands[2] % 256) + 1. + +(define_expand "movmem_short" + [(parallel + [(set (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" "")) + (use (match_operand 2 "nonmemory_operand" "")) + (use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) + (clobber (match_dup 3))])] + "" + "operands[3] = gen_rtx_SCRATCH (Pmode);") + +(define_insn "*movmem_short" + [(set (match_operand:BLK 0 "memory_operand" "=Q,Q,Q,Q") + (match_operand:BLK 1 "memory_operand" "Q,Q,Q,Q")) + (use (match_operand 2 "nonmemory_operand" "n,a,a,a")) + (use (match_operand 3 "immediate_operand" "X,R,X,X")) + (clobber (match_scratch 4 "=X,X,X,&a"))] + "(GET_MODE (operands[2]) == Pmode || GET_MODE (operands[2]) == VOIDmode) + && GET_MODE (operands[4]) == Pmode" + "#" + [(set_attr "type" "cs") + (set_attr "cpu_facility" "*,*,z10,*")]) + +(define_split + [(set (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" "")) + (use (match_operand 2 "const_int_operand" "")) + (use (match_operand 3 "immediate_operand" "")) + (clobber (scratch))] + "reload_completed" + [(parallel + [(set (match_dup 0) (match_dup 1)) + (use (match_dup 2))])] + "operands[2] = GEN_INT ((INTVAL (operands[2]) & 0xff) + 1);") + +(define_split + [(set (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" "")) + (use (match_operand 2 "register_operand" "")) + (use (match_operand 3 "memory_operand" "")) + (clobber (scratch))] + "reload_completed" + [(parallel + [(unspec [(match_dup 2) (match_dup 3) + (const_int 0)] UNSPEC_EXECUTE) + (set (match_dup 0) (match_dup 1)) + (use (const_int 1))])] + "") + +(define_split + [(set (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" "")) + (use (match_operand 2 "register_operand" "")) + (use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) + (clobber (scratch))] + "TARGET_Z10 && reload_completed" + [(parallel + [(unspec [(match_dup 2) (const_int 0) + (label_ref (match_dup 3))] UNSPEC_EXECUTE) + (set (match_dup 0) (match_dup 1)) + (use (const_int 1))])] + "operands[3] = gen_label_rtx ();") + +(define_split + [(set (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" "")) + (use (match_operand 2 "register_operand" "")) + (use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) + (clobber (match_operand 3 "register_operand" ""))] + "reload_completed && TARGET_CPU_ZARCH" + [(set (match_dup 3) (label_ref (match_dup 4))) + (parallel + [(unspec [(match_dup 2) (mem:BLK (match_dup 3)) + (label_ref (match_dup 4))] UNSPEC_EXECUTE) + (set (match_dup 0) (match_dup 1)) + (use (const_int 1))])] + "operands[4] = gen_label_rtx ();") + +; Move a block of arbitrary length. + +(define_expand "movmem_long" + [(parallel + [(clobber (match_dup 2)) + (clobber (match_dup 3)) + (set (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" "")) + (use (match_operand 2 "general_operand" "")) + (use (match_dup 3)) + (clobber (reg:CC CC_REGNUM))])] + "" +{ + enum machine_mode sreg_mode = TARGET_ZARCH ? DImode : SImode; + enum machine_mode dreg_mode = TARGET_ZARCH ? TImode : DImode; + rtx reg0 = gen_reg_rtx (dreg_mode); + rtx reg1 = gen_reg_rtx (dreg_mode); + rtx addr0 = gen_lowpart (Pmode, gen_highpart (sreg_mode, reg0)); + rtx addr1 = gen_lowpart (Pmode, gen_highpart (sreg_mode, reg1)); + rtx len0 = gen_lowpart (Pmode, reg0); + rtx len1 = gen_lowpart (Pmode, reg1); + + emit_clobber (reg0); + emit_move_insn (addr0, force_operand (XEXP (operands[0], 0), NULL_RTX)); + emit_move_insn (len0, operands[2]); + + emit_clobber (reg1); + emit_move_insn (addr1, force_operand (XEXP (operands[1], 0), NULL_RTX)); + emit_move_insn (len1, operands[2]); + + operands[0] = replace_equiv_address_nv (operands[0], addr0); + operands[1] = replace_equiv_address_nv (operands[1], addr1); + operands[2] = reg0; + operands[3] = reg1; +}) + +(define_insn "*movmem_long" + [(clobber (match_operand:<DBL> 0 "register_operand" "=d")) + (clobber (match_operand:<DBL> 1 "register_operand" "=d")) + (set (mem:BLK (subreg:P (match_operand:<DBL> 2 "register_operand" "0") 0)) + (mem:BLK (subreg:P (match_operand:<DBL> 3 "register_operand" "1") 0))) + (use (match_dup 2)) + (use (match_dup 3)) + (clobber (reg:CC CC_REGNUM))] + "TARGET_64BIT || !TARGET_ZARCH" + "mvcle\t%0,%1,0\;jo\t.-4" + [(set_attr "length" "8") + (set_attr "type" "vs")]) + +(define_insn "*movmem_long_31z" + [(clobber (match_operand:TI 0 "register_operand" "=d")) + (clobber (match_operand:TI 1 "register_operand" "=d")) + (set (mem:BLK (subreg:SI (match_operand:TI 2 "register_operand" "0") 4)) + (mem:BLK (subreg:SI (match_operand:TI 3 "register_operand" "1") 4))) + (use (match_dup 2)) + (use (match_dup 3)) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_64BIT && TARGET_ZARCH" + "mvcle\t%0,%1,0\;jo\t.-4" + [(set_attr "length" "8") + (set_attr "type" "vs")]) + + +; +; Test data class. +; + +(define_expand "signbit<mode>2" + [(set (reg:CCZ CC_REGNUM) + (unspec:CCZ [(match_operand:FP_ALL 1 "register_operand" "f") + (match_dup 2)] + UNSPEC_TDC_INSN)) + (set (match_operand:SI 0 "register_operand" "=d") + (unspec:SI [(reg:CCZ CC_REGNUM)] UNSPEC_CCZ_TO_INT))] + "TARGET_HARD_FLOAT" +{ + operands[2] = GEN_INT (S390_TDC_SIGNBIT_SET); +}) + +(define_expand "isinf<mode>2" + [(set (reg:CCZ CC_REGNUM) + (unspec:CCZ [(match_operand:FP_ALL 1 "register_operand" "f") + (match_dup 2)] + UNSPEC_TDC_INSN)) + (set (match_operand:SI 0 "register_operand" "=d") + (unspec:SI [(reg:CCZ CC_REGNUM)] UNSPEC_CCZ_TO_INT))] + "TARGET_HARD_FLOAT" +{ + operands[2] = GEN_INT (S390_TDC_INFINITY); +}) + +; This insn is used to generate all variants of the Test Data Class +; instruction, namely tcxb, tcdb, and tceb. The insn's first operand +; is the register to be tested and the second one is the bit mask +; specifying the required test(s). +; +(define_insn "*TDC_insn_<mode>" + [(set (reg:CCZ CC_REGNUM) + (unspec:CCZ [(match_operand:FP_ALL 0 "register_operand" "f") + (match_operand:SI 1 "const_int_operand")] UNSPEC_TDC_INSN))] + "TARGET_HARD_FLOAT" + "t<_d>c<xde><bt>\t%0,%1" + [(set_attr "op_type" "RXE") + (set_attr "type" "fsimp<mode>")]) + +(define_insn_and_split "*ccz_to_int" + [(set (match_operand:SI 0 "register_operand" "=d") + (unspec:SI [(match_operand:CCZ 1 "register_operand" "0")] + UNSPEC_CCZ_TO_INT))] + "" + "#" + "reload_completed" + [(set (match_dup 0) (lshiftrt:SI (match_dup 0) (const_int 28)))]) + + +; +; setmemM instruction pattern(s). +; + +(define_expand "setmem<mode>" + [(set (match_operand:BLK 0 "memory_operand" "") + (match_operand:QI 2 "general_operand" "")) + (use (match_operand:GPR 1 "general_operand" "")) + (match_operand 3 "" "")] + "" + "s390_expand_setmem (operands[0], operands[1], operands[2]); DONE;") + +; Clear a block that is up to 256 bytes in length. +; The block length is taken as (operands[1] % 256) + 1. + +(define_expand "clrmem_short" + [(parallel + [(set (match_operand:BLK 0 "memory_operand" "") + (const_int 0)) + (use (match_operand 1 "nonmemory_operand" "")) + (use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) + (clobber (match_dup 2)) + (clobber (reg:CC CC_REGNUM))])] + "" + "operands[2] = gen_rtx_SCRATCH (Pmode);") + +(define_insn "*clrmem_short" + [(set (match_operand:BLK 0 "memory_operand" "=Q,Q,Q,Q") + (const_int 0)) + (use (match_operand 1 "nonmemory_operand" "n,a,a,a")) + (use (match_operand 2 "immediate_operand" "X,R,X,X")) + (clobber (match_scratch 3 "=X,X,X,&a")) + (clobber (reg:CC CC_REGNUM))] + "(GET_MODE (operands[1]) == Pmode || GET_MODE (operands[1]) == VOIDmode) + && GET_MODE (operands[3]) == Pmode" + "#" + [(set_attr "type" "cs") + (set_attr "cpu_facility" "*,*,z10,*")]) + +(define_split + [(set (match_operand:BLK 0 "memory_operand" "") + (const_int 0)) + (use (match_operand 1 "const_int_operand" "")) + (use (match_operand 2 "immediate_operand" "")) + (clobber (scratch)) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" + [(parallel + [(set (match_dup 0) (const_int 0)) + (use (match_dup 1)) + (clobber (reg:CC CC_REGNUM))])] + "operands[1] = GEN_INT ((INTVAL (operands[1]) & 0xff) + 1);") + +(define_split + [(set (match_operand:BLK 0 "memory_operand" "") + (const_int 0)) + (use (match_operand 1 "register_operand" "")) + (use (match_operand 2 "memory_operand" "")) + (clobber (scratch)) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" + [(parallel + [(unspec [(match_dup 1) (match_dup 2) + (const_int 0)] UNSPEC_EXECUTE) + (set (match_dup 0) (const_int 0)) + (use (const_int 1)) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_split + [(set (match_operand:BLK 0 "memory_operand" "") + (const_int 0)) + (use (match_operand 1 "register_operand" "")) + (use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) + (clobber (scratch)) + (clobber (reg:CC CC_REGNUM))] + "TARGET_Z10 && reload_completed" + [(parallel + [(unspec [(match_dup 1) (const_int 0) + (label_ref (match_dup 3))] UNSPEC_EXECUTE) + (set (match_dup 0) (const_int 0)) + (use (const_int 1)) + (clobber (reg:CC CC_REGNUM))])] + "operands[3] = gen_label_rtx ();") + +(define_split + [(set (match_operand:BLK 0 "memory_operand" "") + (const_int 0)) + (use (match_operand 1 "register_operand" "")) + (use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) + (clobber (match_operand 2 "register_operand" "")) + (clobber (reg:CC CC_REGNUM))] + "reload_completed && TARGET_CPU_ZARCH" + [(set (match_dup 2) (label_ref (match_dup 3))) + (parallel + [(unspec [(match_dup 1) (mem:BLK (match_dup 2)) + (label_ref (match_dup 3))] UNSPEC_EXECUTE) + (set (match_dup 0) (const_int 0)) + (use (const_int 1)) + (clobber (reg:CC CC_REGNUM))])] + "operands[3] = gen_label_rtx ();") + +; Initialize a block of arbitrary length with (operands[2] % 256). + +(define_expand "setmem_long" + [(parallel + [(clobber (match_dup 1)) + (set (match_operand:BLK 0 "memory_operand" "") + (match_operand 2 "shift_count_or_setmem_operand" "")) + (use (match_operand 1 "general_operand" "")) + (use (match_dup 3)) + (clobber (reg:CC CC_REGNUM))])] + "" +{ + enum machine_mode sreg_mode = TARGET_ZARCH ? DImode : SImode; + enum machine_mode dreg_mode = TARGET_ZARCH ? TImode : DImode; + rtx reg0 = gen_reg_rtx (dreg_mode); + rtx reg1 = gen_reg_rtx (dreg_mode); + rtx addr0 = gen_lowpart (Pmode, gen_highpart (sreg_mode, reg0)); + rtx len0 = gen_lowpart (Pmode, reg0); + + emit_clobber (reg0); + emit_move_insn (addr0, force_operand (XEXP (operands[0], 0), NULL_RTX)); + emit_move_insn (len0, operands[1]); + + emit_move_insn (reg1, const0_rtx); + + operands[0] = replace_equiv_address_nv (operands[0], addr0); + operands[1] = reg0; + operands[3] = reg1; +}) + +(define_insn "*setmem_long" + [(clobber (match_operand:<DBL> 0 "register_operand" "=d")) + (set (mem:BLK (subreg:P (match_operand:<DBL> 3 "register_operand" "0") 0)) + (match_operand 2 "shift_count_or_setmem_operand" "Y")) + (use (match_dup 3)) + (use (match_operand:<DBL> 1 "register_operand" "d")) + (clobber (reg:CC CC_REGNUM))] + "TARGET_64BIT || !TARGET_ZARCH" + "mvcle\t%0,%1,%Y2\;jo\t.-4" + [(set_attr "length" "8") + (set_attr "type" "vs")]) + +(define_insn "*setmem_long_and" + [(clobber (match_operand:<DBL> 0 "register_operand" "=d")) + (set (mem:BLK (subreg:P (match_operand:<DBL> 3 "register_operand" "0") 0)) + (and (match_operand 2 "shift_count_or_setmem_operand" "Y") + (match_operand 4 "const_int_operand" "n"))) + (use (match_dup 3)) + (use (match_operand:<DBL> 1 "register_operand" "d")) + (clobber (reg:CC CC_REGNUM))] + "(TARGET_64BIT || !TARGET_ZARCH) && + (INTVAL (operands[4]) & 255) == 255" + "mvcle\t%0,%1,%Y2\;jo\t.-4" + [(set_attr "length" "8") + (set_attr "type" "vs")]) + +(define_insn "*setmem_long_31z" + [(clobber (match_operand:TI 0 "register_operand" "=d")) + (set (mem:BLK (subreg:SI (match_operand:TI 3 "register_operand" "0") 4)) + (match_operand 2 "shift_count_or_setmem_operand" "Y")) + (use (match_dup 3)) + (use (match_operand:TI 1 "register_operand" "d")) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_64BIT && TARGET_ZARCH" + "mvcle\t%0,%1,%Y2\;jo\t.-4" + [(set_attr "length" "8") + (set_attr "type" "vs")]) + +; +; cmpmemM instruction pattern(s). +; + +(define_expand "cmpmemsi" + [(set (match_operand:SI 0 "register_operand" "") + (compare:SI (match_operand:BLK 1 "memory_operand" "") + (match_operand:BLK 2 "memory_operand" "") ) ) + (use (match_operand:SI 3 "general_operand" "")) + (use (match_operand:SI 4 "" ""))] + "" + "s390_expand_cmpmem (operands[0], operands[1], + operands[2], operands[3]); DONE;") + +; Compare a block that is up to 256 bytes in length. +; The block length is taken as (operands[2] % 256) + 1. + +(define_expand "cmpmem_short" + [(parallel + [(set (reg:CCU CC_REGNUM) + (compare:CCU (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" ""))) + (use (match_operand 2 "nonmemory_operand" "")) + (use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) + (clobber (match_dup 3))])] + "" + "operands[3] = gen_rtx_SCRATCH (Pmode);") + +(define_insn "*cmpmem_short" + [(set (reg:CCU CC_REGNUM) + (compare:CCU (match_operand:BLK 0 "memory_operand" "Q,Q,Q,Q") + (match_operand:BLK 1 "memory_operand" "Q,Q,Q,Q"))) + (use (match_operand 2 "nonmemory_operand" "n,a,a,a")) + (use (match_operand 3 "immediate_operand" "X,R,X,X")) + (clobber (match_scratch 4 "=X,X,X,&a"))] + "(GET_MODE (operands[2]) == Pmode || GET_MODE (operands[2]) == VOIDmode) + && GET_MODE (operands[4]) == Pmode" + "#" + [(set_attr "type" "cs") + (set_attr "cpu_facility" "*,*,z10,*")]) + +(define_split + [(set (reg:CCU CC_REGNUM) + (compare:CCU (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" ""))) + (use (match_operand 2 "const_int_operand" "")) + (use (match_operand 3 "immediate_operand" "")) + (clobber (scratch))] + "reload_completed" + [(parallel + [(set (reg:CCU CC_REGNUM) (compare:CCU (match_dup 0) (match_dup 1))) + (use (match_dup 2))])] + "operands[2] = GEN_INT ((INTVAL (operands[2]) & 0xff) + 1);") + +(define_split + [(set (reg:CCU CC_REGNUM) + (compare:CCU (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" ""))) + (use (match_operand 2 "register_operand" "")) + (use (match_operand 3 "memory_operand" "")) + (clobber (scratch))] + "reload_completed" + [(parallel + [(unspec [(match_dup 2) (match_dup 3) + (const_int 0)] UNSPEC_EXECUTE) + (set (reg:CCU CC_REGNUM) (compare:CCU (match_dup 0) (match_dup 1))) + (use (const_int 1))])] + "") + +(define_split + [(set (reg:CCU CC_REGNUM) + (compare:CCU (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" ""))) + (use (match_operand 2 "register_operand" "")) + (use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) + (clobber (scratch))] + "TARGET_Z10 && reload_completed" + [(parallel + [(unspec [(match_dup 2) (const_int 0) + (label_ref (match_dup 4))] UNSPEC_EXECUTE) + (set (reg:CCU CC_REGNUM) (compare:CCU (match_dup 0) (match_dup 1))) + (use (const_int 1))])] + "operands[4] = gen_label_rtx ();") + +(define_split + [(set (reg:CCU CC_REGNUM) + (compare:CCU (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" ""))) + (use (match_operand 2 "register_operand" "")) + (use (const:BLK (unspec:BLK [(const_int 0)] UNSPEC_INSN))) + (clobber (match_operand 3 "register_operand" ""))] + "reload_completed && TARGET_CPU_ZARCH" + [(set (match_dup 3) (label_ref (match_dup 4))) + (parallel + [(unspec [(match_dup 2) (mem:BLK (match_dup 3)) + (label_ref (match_dup 4))] UNSPEC_EXECUTE) + (set (reg:CCU CC_REGNUM) (compare:CCU (match_dup 0) (match_dup 1))) + (use (const_int 1))])] + "operands[4] = gen_label_rtx ();") + +; Compare a block of arbitrary length. + +(define_expand "cmpmem_long" + [(parallel + [(clobber (match_dup 2)) + (clobber (match_dup 3)) + (set (reg:CCU CC_REGNUM) + (compare:CCU (match_operand:BLK 0 "memory_operand" "") + (match_operand:BLK 1 "memory_operand" ""))) + (use (match_operand 2 "general_operand" "")) + (use (match_dup 3))])] + "" +{ + enum machine_mode sreg_mode = TARGET_ZARCH ? DImode : SImode; + enum machine_mode dreg_mode = TARGET_ZARCH ? TImode : DImode; + rtx reg0 = gen_reg_rtx (dreg_mode); + rtx reg1 = gen_reg_rtx (dreg_mode); + rtx addr0 = gen_lowpart (Pmode, gen_highpart (sreg_mode, reg0)); + rtx addr1 = gen_lowpart (Pmode, gen_highpart (sreg_mode, reg1)); + rtx len0 = gen_lowpart (Pmode, reg0); + rtx len1 = gen_lowpart (Pmode, reg1); + + emit_clobber (reg0); + emit_move_insn (addr0, force_operand (XEXP (operands[0], 0), NULL_RTX)); + emit_move_insn (len0, operands[2]); + + emit_clobber (reg1); + emit_move_insn (addr1, force_operand (XEXP (operands[1], 0), NULL_RTX)); + emit_move_insn (len1, operands[2]); + + operands[0] = replace_equiv_address_nv (operands[0], addr0); + operands[1] = replace_equiv_address_nv (operands[1], addr1); + operands[2] = reg0; + operands[3] = reg1; +}) + +(define_insn "*cmpmem_long" + [(clobber (match_operand:<DBL> 0 "register_operand" "=d")) + (clobber (match_operand:<DBL> 1 "register_operand" "=d")) + (set (reg:CCU CC_REGNUM) + (compare:CCU (mem:BLK (subreg:P (match_operand:<DBL> 2 "register_operand" "0") 0)) + (mem:BLK (subreg:P (match_operand:<DBL> 3 "register_operand" "1") 0)))) + (use (match_dup 2)) + (use (match_dup 3))] + "TARGET_64BIT || !TARGET_ZARCH" + "clcle\t%0,%1,0\;jo\t.-4" + [(set_attr "length" "8") + (set_attr "type" "vs")]) + +(define_insn "*cmpmem_long_31z" + [(clobber (match_operand:TI 0 "register_operand" "=d")) + (clobber (match_operand:TI 1 "register_operand" "=d")) + (set (reg:CCU CC_REGNUM) + (compare:CCU (mem:BLK (subreg:SI (match_operand:TI 2 "register_operand" "0") 4)) + (mem:BLK (subreg:SI (match_operand:TI 3 "register_operand" "1") 4)))) + (use (match_dup 2)) + (use (match_dup 3))] + "!TARGET_64BIT && TARGET_ZARCH" + "clcle\t%0,%1,0\;jo\t.-4" + [(set_attr "op_type" "NN") + (set_attr "type" "vs") + (set_attr "length" "8")]) + +; Convert CCUmode condition code to integer. +; Result is zero if EQ, positive if LTU, negative if GTU. + +(define_insn_and_split "cmpint" + [(set (match_operand:SI 0 "register_operand" "=d") + (unspec:SI [(match_operand:CCU 1 "register_operand" "0")] + UNSPEC_CCU_TO_INT)) + (clobber (reg:CC CC_REGNUM))] + "" + "#" + "reload_completed" + [(set (match_dup 0) (ashift:SI (match_dup 0) (const_int 2))) + (parallel + [(set (match_dup 0) (ashiftrt:SI (match_dup 0) (const_int 30))) + (clobber (reg:CC CC_REGNUM))])]) + +(define_insn_and_split "*cmpint_cc" + [(set (reg CC_REGNUM) + (compare (unspec:SI [(match_operand:CCU 1 "register_operand" "0")] + UNSPEC_CCU_TO_INT) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=d") + (unspec:SI [(match_dup 1)] UNSPEC_CCU_TO_INT))] + "s390_match_ccmode (insn, CCSmode)" + "#" + "&& reload_completed" + [(set (match_dup 0) (ashift:SI (match_dup 0) (const_int 2))) + (parallel + [(set (match_dup 2) (match_dup 3)) + (set (match_dup 0) (ashiftrt:SI (match_dup 0) (const_int 30)))])] +{ + rtx result = gen_rtx_ASHIFTRT (SImode, operands[0], GEN_INT (30)); + operands[2] = SET_DEST (XVECEXP (PATTERN (curr_insn), 0, 0)); + operands[3] = gen_rtx_COMPARE (GET_MODE (operands[2]), result, const0_rtx); +}) + +(define_insn_and_split "*cmpint_sign" + [(set (match_operand:DI 0 "register_operand" "=d") + (sign_extend:DI (unspec:SI [(match_operand:CCU 1 "register_operand" "0")] + UNSPEC_CCU_TO_INT))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" + "#" + "&& reload_completed" + [(set (match_dup 0) (ashift:DI (match_dup 0) (const_int 34))) + (parallel + [(set (match_dup 0) (ashiftrt:DI (match_dup 0) (const_int 62))) + (clobber (reg:CC CC_REGNUM))])]) + +(define_insn_and_split "*cmpint_sign_cc" + [(set (reg CC_REGNUM) + (compare (ashiftrt:DI (ashift:DI (subreg:DI + (unspec:SI [(match_operand:CCU 1 "register_operand" "0")] + UNSPEC_CCU_TO_INT) 0) + (const_int 32)) (const_int 32)) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d") + (sign_extend:DI (unspec:SI [(match_dup 1)] UNSPEC_CCU_TO_INT)))] + "s390_match_ccmode (insn, CCSmode) && TARGET_ZARCH" + "#" + "&& reload_completed" + [(set (match_dup 0) (ashift:DI (match_dup 0) (const_int 34))) + (parallel + [(set (match_dup 2) (match_dup 3)) + (set (match_dup 0) (ashiftrt:DI (match_dup 0) (const_int 62)))])] +{ + rtx result = gen_rtx_ASHIFTRT (DImode, operands[0], GEN_INT (62)); + operands[2] = SET_DEST (XVECEXP (PATTERN (curr_insn), 0, 0)); + operands[3] = gen_rtx_COMPARE (GET_MODE (operands[2]), result, const0_rtx); +}) + + +;; +;;- Conversion instructions. +;; + +(define_insn "*sethighpartsi" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (unspec:SI [(match_operand:BLK 1 "s_operand" "Q,S") + (match_operand 2 "const_int_operand" "n,n")] UNSPEC_ICM)) + (clobber (reg:CC CC_REGNUM))] + "" + "@ + icm\t%0,%2,%S1 + icmy\t%0,%2,%S1" + [(set_attr "op_type" "RS,RSY") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + +(define_insn "*sethighpartdi_64" + [(set (match_operand:DI 0 "register_operand" "=d") + (unspec:DI [(match_operand:BLK 1 "s_operand" "QS") + (match_operand 2 "const_int_operand" "n")] UNSPEC_ICM)) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" + "icmh\t%0,%2,%S1" + [(set_attr "op_type" "RSY") + (set_attr "z10prop" "z10_super")]) + +(define_insn "*sethighpartdi_31" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (unspec:DI [(match_operand:BLK 1 "s_operand" "Q,S") + (match_operand 2 "const_int_operand" "n,n")] UNSPEC_ICM)) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH" + "@ + icm\t%0,%2,%S1 + icmy\t%0,%2,%S1" + [(set_attr "op_type" "RS,RSY") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + + +(define_insn_and_split "*extzv<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d") + (zero_extract:GPR (match_operand:QI 1 "s_operand" "QS") + (match_operand 2 "const_int_operand" "n") + (const_int 0))) + (clobber (reg:CC CC_REGNUM))] + "INTVAL (operands[2]) > 0 + && INTVAL (operands[2]) <= GET_MODE_BITSIZE (SImode)" + "#" + "&& reload_completed" + [(parallel + [(set (match_dup 0) (unspec:GPR [(match_dup 1) (match_dup 3)] UNSPEC_ICM)) + (clobber (reg:CC CC_REGNUM))]) + (set (match_dup 0) (lshiftrt:GPR (match_dup 0) (match_dup 2)))] +{ + int bitsize = INTVAL (operands[2]); + int size = (bitsize - 1) / BITS_PER_UNIT + 1; /* round up */ + int mask = ((1ul << size) - 1) << (GET_MODE_SIZE (SImode) - size); + + operands[1] = adjust_address (operands[1], BLKmode, 0); + set_mem_size (operands[1], GEN_INT (size)); + operands[2] = GEN_INT (GET_MODE_BITSIZE (<MODE>mode) - bitsize); + operands[3] = GEN_INT (mask); +}) + +(define_insn_and_split "*extv<mode>" + [(set (match_operand:GPR 0 "register_operand" "=d") + (sign_extract:GPR (match_operand:QI 1 "s_operand" "QS") + (match_operand 2 "const_int_operand" "n") + (const_int 0))) + (clobber (reg:CC CC_REGNUM))] + "INTVAL (operands[2]) > 0 + && INTVAL (operands[2]) <= GET_MODE_BITSIZE (SImode)" + "#" + "&& reload_completed" + [(parallel + [(set (match_dup 0) (unspec:GPR [(match_dup 1) (match_dup 3)] UNSPEC_ICM)) + (clobber (reg:CC CC_REGNUM))]) + (parallel + [(set (match_dup 0) (ashiftrt:GPR (match_dup 0) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] +{ + int bitsize = INTVAL (operands[2]); + int size = (bitsize - 1) / BITS_PER_UNIT + 1; /* round up */ + int mask = ((1ul << size) - 1) << (GET_MODE_SIZE (SImode) - size); + + operands[1] = adjust_address (operands[1], BLKmode, 0); + set_mem_size (operands[1], GEN_INT (size)); + operands[2] = GEN_INT (GET_MODE_BITSIZE (<MODE>mode) - bitsize); + operands[3] = GEN_INT (mask); +}) + +; +; insv instruction patterns +; + +(define_expand "insv" + [(set (zero_extract (match_operand 0 "nonimmediate_operand" "") + (match_operand 1 "const_int_operand" "") + (match_operand 2 "const_int_operand" "")) + (match_operand 3 "general_operand" ""))] + "" +{ + if (s390_expand_insv (operands[0], operands[1], operands[2], operands[3])) + DONE; + FAIL; +}) + +(define_insn "*insv<mode>_z10" + [(set (zero_extract:GPR (match_operand:GPR 0 "nonimmediate_operand" "+d") + (match_operand 1 "const_int_operand" "I") + (match_operand 2 "const_int_operand" "I")) + (match_operand:GPR 3 "nonimmediate_operand" "d")) + (clobber (reg:CC CC_REGNUM))] + "TARGET_Z10 + && (INTVAL (operands[1]) + INTVAL (operands[2])) <= + GET_MODE_BITSIZE (<MODE>mode)" +{ + int start = INTVAL (operands[2]); + int size = INTVAL (operands[1]); + int offset = 64 - GET_MODE_BITSIZE (<MODE>mode); + + operands[2] = GEN_INT (offset + start); /* start bit position */ + operands[1] = GEN_INT (offset + start + size - 1); /* end bit position */ + operands[4] = GEN_INT (GET_MODE_BITSIZE (<MODE>mode) - + start - size); /* left shift count */ + + return "risbg\t%0,%3,%b2,%b1,%b4"; +} + [(set_attr "op_type" "RIE") + (set_attr "z10prop" "z10_super_E1")]) + +; and op1 with a mask being 1 for the selected bits and 0 for the rest +; and op3=op0 with a mask being 0 for the selected bits and 1 for the rest +(define_insn "*insv<mode>_z10_noshift" + [(set (match_operand:GPR 0 "nonimmediate_operand" "=d") + (ior:GPR (and:GPR (match_operand:GPR 1 "nonimmediate_operand" "d") + (match_operand 2 "const_int_operand" "n")) + (and:GPR (match_operand:GPR 3 "nonimmediate_operand" "0") + (match_operand 4 "const_int_operand" "n")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_Z10 + && s390_contiguous_bitmask_p (INTVAL (operands[2]), + GET_MODE_BITSIZE (<MODE>mode), NULL, NULL) + && INTVAL (operands[2]) == ~(INTVAL (operands[4]))" + +{ + int start; + int size; + + s390_contiguous_bitmask_p (INTVAL (operands[2]), + GET_MODE_BITSIZE (<MODE>mode), &start, &size); + + operands[5] = GEN_INT (64 - start - size); /* start bit position */ + operands[6] = GEN_INT (64 - 1 - start); /* end bit position */ + operands[7] = const0_rtx; /* left shift count */ + + return "risbg\t%0,%1,%b5,%b6,%b7"; +} + [(set_attr "op_type" "RIE") + (set_attr "z10prop" "z10_super_E1")]) + +; and op1 with a mask being 1 for the selected bits and 0 for the rest +(define_insn "*insv<mode>_or_z10_noshift" + [(set (match_operand:GPR 0 "nonimmediate_operand" "=d") + (ior:GPR (and:GPR (match_operand:GPR 1 "nonimmediate_operand" "d") + (match_operand 2 "const_int_operand" "n")) + (match_operand:GPR 3 "nonimmediate_operand" "0"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_Z10 + && s390_contiguous_bitmask_p (INTVAL (operands[2]), + GET_MODE_BITSIZE (<MODE>mode), NULL, NULL)" +{ + int start; + int size; + + s390_contiguous_bitmask_p (INTVAL (operands[2]), + GET_MODE_BITSIZE (<MODE>mode), &start, &size); + + operands[4] = GEN_INT (64 - start - size); /* start bit position */ + operands[5] = GEN_INT (64 - 1 - start); /* end bit position */ + operands[6] = const0_rtx; /* left shift count */ + + return "rosbg\t%0,%1,%b4,%b5,%b6"; +} + [(set_attr "op_type" "RIE")]) + +(define_insn "*insv<mode>_mem_reg" + [(set (zero_extract:W (match_operand:QI 0 "memory_operand" "+Q,S") + (match_operand 1 "const_int_operand" "n,n") + (const_int 0)) + (match_operand:W 2 "register_operand" "d,d"))] + "INTVAL (operands[1]) > 0 + && INTVAL (operands[1]) <= GET_MODE_BITSIZE (SImode) + && INTVAL (operands[1]) % BITS_PER_UNIT == 0" +{ + int size = INTVAL (operands[1]) / BITS_PER_UNIT; + + operands[1] = GEN_INT ((1ul << size) - 1); + return (which_alternative == 0) ? "stcm\t%2,%1,%S0" + : "stcmy\t%2,%1,%S0"; +} + [(set_attr "op_type" "RS,RSY") + (set_attr "z10prop" "z10_super,z10_super")]) + +(define_insn "*insvdi_mem_reghigh" + [(set (zero_extract:DI (match_operand:QI 0 "memory_operand" "+QS") + (match_operand 1 "const_int_operand" "n") + (const_int 0)) + (lshiftrt:DI (match_operand:DI 2 "register_operand" "d") + (const_int 32)))] + "TARGET_ZARCH + && INTVAL (operands[1]) > 0 + && INTVAL (operands[1]) <= GET_MODE_BITSIZE (SImode) + && INTVAL (operands[1]) % BITS_PER_UNIT == 0" +{ + int size = INTVAL (operands[1]) / BITS_PER_UNIT; + + operands[1] = GEN_INT ((1ul << size) - 1); + return "stcmh\t%2,%1,%S0"; +} +[(set_attr "op_type" "RSY") + (set_attr "z10prop" "z10_super")]) + +(define_insn "*insvdi_reg_imm" + [(set (zero_extract:DI (match_operand:DI 0 "register_operand" "+d") + (const_int 16) + (match_operand 1 "const_int_operand" "n")) + (match_operand:DI 2 "const_int_operand" "n"))] + "TARGET_ZARCH + && INTVAL (operands[1]) >= 0 + && INTVAL (operands[1]) < BITS_PER_WORD + && INTVAL (operands[1]) % 16 == 0" +{ + switch (BITS_PER_WORD - INTVAL (operands[1])) + { + case 64: return "iihh\t%0,%x2"; break; + case 48: return "iihl\t%0,%x2"; break; + case 32: return "iilh\t%0,%x2"; break; + case 16: return "iill\t%0,%x2"; break; + default: gcc_unreachable(); + } +} + [(set_attr "op_type" "RI") + (set_attr "z10prop" "z10_super_E1")]) + +; Update the left-most 32 bit of a DI. +(define_insn "*insv_h_di_reg_extimm" + [(set (zero_extract:DI (match_operand:DI 0 "register_operand" "+d") + (const_int 32) + (const_int 0)) + (match_operand:DI 1 "const_int_operand" "n"))] + "TARGET_EXTIMM" + "iihf\t%0,%o1" + [(set_attr "op_type" "RIL") + (set_attr "z10prop" "z10_fwd_E1")]) + +; Update the right-most 32 bit of a DI, or the whole of a SI. +(define_insn "*insv_l<mode>_reg_extimm" + [(set (zero_extract:P (match_operand:P 0 "register_operand" "+d") + (const_int 32) + (match_operand 1 "const_int_operand" "n")) + (match_operand:P 2 "const_int_operand" "n"))] + "TARGET_EXTIMM + && BITS_PER_WORD - INTVAL (operands[1]) == 32" + "iilf\t%0,%o2" + [(set_attr "op_type" "RIL") + (set_attr "z10prop" "z10_fwd_A1")]) + +; +; extendsidi2 instruction pattern(s). +; + +(define_expand "extendsidi2" + [(set (match_operand:DI 0 "register_operand" "") + (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")))] + "" +{ + if (!TARGET_ZARCH) + { + emit_clobber (operands[0]); + emit_move_insn (gen_highpart (SImode, operands[0]), operands[1]); + emit_move_insn (gen_lowpart (SImode, operands[0]), const0_rtx); + emit_insn (gen_ashrdi3 (operands[0], operands[0], GEN_INT (32))); + DONE; + } +}) + +(define_insn "*extendsidi2" + [(set (match_operand:DI 0 "register_operand" "=d,d,d") + (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,RT,b")))] + "TARGET_ZARCH" + "@ + lgfr\t%0,%1 + lgf\t%0,%1 + lgfrl\t%0,%1" + [(set_attr "op_type" "RRE,RXY,RIL") + (set_attr "type" "*,*,larl") + (set_attr "cpu_facility" "*,*,z10") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,z10_super_E1")]) + +; +; extend(hi|qi)(si|di)2 instruction pattern(s). +; + +(define_expand "extend<HQI:mode><DSI:mode>2" + [(set (match_operand:DSI 0 "register_operand" "") + (sign_extend:DSI (match_operand:HQI 1 "nonimmediate_operand" "")))] + "" +{ + if (<DSI:MODE>mode == DImode && !TARGET_ZARCH) + { + rtx tmp = gen_reg_rtx (SImode); + emit_insn (gen_extend<HQI:mode>si2 (tmp, operands[1])); + emit_insn (gen_extendsidi2 (operands[0], tmp)); + DONE; + } + else if (!TARGET_EXTIMM) + { + rtx bitcount = GEN_INT (GET_MODE_BITSIZE (<DSI:MODE>mode) - + GET_MODE_BITSIZE (<HQI:MODE>mode)); + + operands[1] = gen_lowpart (<DSI:MODE>mode, operands[1]); + emit_insn (gen_ashl<DSI:mode>3 (operands[0], operands[1], bitcount)); + emit_insn (gen_ashr<DSI:mode>3 (operands[0], operands[0], bitcount)); + DONE; + } +}) + +; +; extendhidi2 instruction pattern(s). +; + +(define_insn "*extendhidi2_extimm" + [(set (match_operand:DI 0 "register_operand" "=d,d,d") + (sign_extend:DI (match_operand:HI 1 "general_operand" "d,RT,b")))] + "TARGET_ZARCH && TARGET_EXTIMM" + "@ + lghr\t%0,%1 + lgh\t%0,%1 + lghrl\t%0,%1" + [(set_attr "op_type" "RRE,RXY,RIL") + (set_attr "type" "*,*,larl") + (set_attr "cpu_facility" "extimm,extimm,z10") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,z10_super_E1")]) + +(define_insn "*extendhidi2" + [(set (match_operand:DI 0 "register_operand" "=d") + (sign_extend:DI (match_operand:HI 1 "memory_operand" "RT")))] + "TARGET_ZARCH" + "lgh\t%0,%1" + [(set_attr "op_type" "RXY") + (set_attr "z10prop" "z10_super_E1")]) + +; +; extendhisi2 instruction pattern(s). +; + +(define_insn "*extendhisi2_extimm" + [(set (match_operand:SI 0 "register_operand" "=d,d,d,d") + (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" " d,R,T,b")))] + "TARGET_EXTIMM" + "@ + lhr\t%0,%1 + lh\t%0,%1 + lhy\t%0,%1 + lhrl\t%0,%1" + [(set_attr "op_type" "RRE,RX,RXY,RIL") + (set_attr "type" "*,*,*,larl") + (set_attr "cpu_facility" "extimm,extimm,extimm,z10") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,z10_super_E1,z10_super_E1")]) + +(define_insn "*extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (sign_extend:SI (match_operand:HI 1 "memory_operand" "R,T")))] + "!TARGET_EXTIMM" + "@ + lh\t%0,%1 + lhy\t%0,%1" + [(set_attr "op_type" "RX,RXY") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + +; +; extendqi(si|di)2 instruction pattern(s). +; + +; lbr, lgbr, lb, lgb +(define_insn "*extendqi<mode>2_extimm" + [(set (match_operand:GPR 0 "register_operand" "=d,d") + (sign_extend:GPR (match_operand:QI 1 "nonimmediate_operand" "d,RT")))] + "TARGET_EXTIMM" + "@ + l<g>br\t%0,%1 + l<g>b\t%0,%1" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + +; lb, lgb +(define_insn "*extendqi<mode>2" + [(set (match_operand:GPR 0 "register_operand" "=d") + (sign_extend:GPR (match_operand:QI 1 "memory_operand" "RT")))] + "!TARGET_EXTIMM && TARGET_LONG_DISPLACEMENT" + "l<g>b\t%0,%1" + [(set_attr "op_type" "RXY") + (set_attr "z10prop" "z10_super_E1")]) + +(define_insn_and_split "*extendqi<mode>2_short_displ" + [(set (match_operand:GPR 0 "register_operand" "=d") + (sign_extend:GPR (match_operand:QI 1 "s_operand" "Q"))) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_EXTIMM && !TARGET_LONG_DISPLACEMENT" + "#" + "&& reload_completed" + [(parallel + [(set (match_dup 0) (unspec:GPR [(match_dup 1) (const_int 8)] UNSPEC_ICM)) + (clobber (reg:CC CC_REGNUM))]) + (parallel + [(set (match_dup 0) (ashiftrt:GPR (match_dup 0) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))])] +{ + operands[1] = adjust_address (operands[1], BLKmode, 0); + set_mem_size (operands[1], GEN_INT (GET_MODE_SIZE (QImode))); + operands[2] = GEN_INT (GET_MODE_BITSIZE (<MODE>mode) + - GET_MODE_BITSIZE (QImode)); +}) + +; +; zero_extendsidi2 instruction pattern(s). +; + +(define_expand "zero_extendsidi2" + [(set (match_operand:DI 0 "register_operand" "") + (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")))] + "" +{ + if (!TARGET_ZARCH) + { + emit_clobber (operands[0]); + emit_move_insn (gen_lowpart (SImode, operands[0]), operands[1]); + emit_move_insn (gen_highpart (SImode, operands[0]), const0_rtx); + DONE; + } +}) + +(define_insn "*zero_extendsidi2" + [(set (match_operand:DI 0 "register_operand" "=d,d,d") + (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,RT,b")))] + "TARGET_ZARCH" + "@ + llgfr\t%0,%1 + llgf\t%0,%1 + llgfrl\t%0,%1" + [(set_attr "op_type" "RRE,RXY,RIL") + (set_attr "type" "*,*,larl") + (set_attr "cpu_facility" "*,*,z10") + (set_attr "z10prop" "z10_fwd_E1,z10_fwd_A3,z10_fwd_A3")]) + +; +; LLGT-type instructions (zero-extend from 31 bit to 64 bit). +; + +(define_insn "*llgt_sidi" + [(set (match_operand:DI 0 "register_operand" "=d") + (and:DI (subreg:DI (match_operand:SI 1 "memory_operand" "RT") 0) + (const_int 2147483647)))] + "TARGET_ZARCH" + "llgt\t%0,%1" + [(set_attr "op_type" "RXE") + (set_attr "z10prop" "z10_super_E1")]) + +(define_insn_and_split "*llgt_sidi_split" + [(set (match_operand:DI 0 "register_operand" "=d") + (and:DI (subreg:DI (match_operand:SI 1 "memory_operand" "RT") 0) + (const_int 2147483647))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" + "#" + "&& reload_completed" + [(set (match_dup 0) + (and:DI (subreg:DI (match_dup 1) 0) + (const_int 2147483647)))] + "") + +(define_insn "*llgt_sisi" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (and:SI (match_operand:SI 1 "nonimmediate_operand" "d,RT") + (const_int 2147483647)))] + "TARGET_ZARCH" + "@ + llgtr\t%0,%1 + llgt\t%0,%1" + [(set_attr "op_type" "RRE,RXE") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + +(define_insn "*llgt_didi" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (and:DI (match_operand:DI 1 "nonimmediate_operand" "d,o") + (const_int 2147483647)))] + "TARGET_ZARCH" + "@ + llgtr\t%0,%1 + llgt\t%0,%N1" + [(set_attr "op_type" "RRE,RXE") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + +(define_split + [(set (match_operand:DSI 0 "register_operand" "") + (and:DSI (match_operand:DSI 1 "nonimmediate_operand" "") + (const_int 2147483647))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH && reload_completed" + [(set (match_dup 0) + (and:DSI (match_dup 1) + (const_int 2147483647)))] + "") + +; +; zero_extend(hi|qi)(si|di)2 instruction pattern(s). +; + +(define_expand "zero_extend<mode>di2" + [(set (match_operand:DI 0 "register_operand" "") + (zero_extend:DI (match_operand:HQI 1 "nonimmediate_operand" "")))] + "" +{ + if (!TARGET_ZARCH) + { + rtx tmp = gen_reg_rtx (SImode); + emit_insn (gen_zero_extend<mode>si2 (tmp, operands[1])); + emit_insn (gen_zero_extendsidi2 (operands[0], tmp)); + DONE; + } + else if (!TARGET_EXTIMM) + { + rtx bitcount = GEN_INT (GET_MODE_BITSIZE(DImode) - + GET_MODE_BITSIZE(<MODE>mode)); + operands[1] = gen_lowpart (DImode, operands[1]); + emit_insn (gen_ashldi3 (operands[0], operands[1], bitcount)); + emit_insn (gen_lshrdi3 (operands[0], operands[0], bitcount)); + DONE; + } +}) + +(define_expand "zero_extend<mode>si2" + [(set (match_operand:SI 0 "register_operand" "") + (zero_extend:SI (match_operand:HQI 1 "nonimmediate_operand" "")))] + "" +{ + if (!TARGET_EXTIMM) + { + operands[1] = gen_lowpart (SImode, operands[1]); + emit_insn (gen_andsi3 (operands[0], operands[1], + GEN_INT ((1 << GET_MODE_BITSIZE(<MODE>mode)) - 1))); + DONE; + } +}) + +; llhrl, llghrl +(define_insn "*zero_extendhi<mode>2_z10" + [(set (match_operand:GPR 0 "register_operand" "=d,d,d") + (zero_extend:GPR (match_operand:HI 1 "nonimmediate_operand" "d,RT,b")))] + "TARGET_Z10" + "@ + ll<g>hr\t%0,%1 + ll<g>h\t%0,%1 + ll<g>hrl\t%0,%1" + [(set_attr "op_type" "RXY,RRE,RIL") + (set_attr "type" "*,*,larl") + (set_attr "cpu_facility" "*,*,z10") + (set_attr "z10prop" "z10_super_E1,z10_fwd_A3,z10_fwd_A3")]) + +; llhr, llcr, llghr, llgcr, llh, llc, llgh, llgc +(define_insn "*zero_extend<HQI:mode><GPR:mode>2_extimm" + [(set (match_operand:GPR 0 "register_operand" "=d,d") + (zero_extend:GPR (match_operand:HQI 1 "nonimmediate_operand" "d,RT")))] + "TARGET_EXTIMM" + "@ + ll<g><hc>r\t%0,%1 + ll<g><hc>\t%0,%1" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_super_E1,z10_fwd_A3")]) + +; llgh, llgc +(define_insn "*zero_extend<HQI:mode><GPR:mode>2" + [(set (match_operand:GPR 0 "register_operand" "=d") + (zero_extend:GPR (match_operand:HQI 1 "memory_operand" "RT")))] + "TARGET_ZARCH && !TARGET_EXTIMM" + "llg<hc>\t%0,%1" + [(set_attr "op_type" "RXY") + (set_attr "z10prop" "z10_fwd_A3")]) + +(define_insn_and_split "*zero_extendhisi2_31" + [(set (match_operand:SI 0 "register_operand" "=&d") + (zero_extend:SI (match_operand:HI 1 "s_operand" "QS"))) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH" + "#" + "&& reload_completed" + [(set (match_dup 0) (const_int 0)) + (parallel + [(set (strict_low_part (match_dup 2)) (match_dup 1)) + (clobber (reg:CC CC_REGNUM))])] + "operands[2] = gen_lowpart (HImode, operands[0]);") + +(define_insn_and_split "*zero_extendqisi2_31" + [(set (match_operand:SI 0 "register_operand" "=&d") + (zero_extend:SI (match_operand:QI 1 "memory_operand" "RT")))] + "!TARGET_ZARCH" + "#" + "&& reload_completed" + [(set (match_dup 0) (const_int 0)) + (set (strict_low_part (match_dup 2)) (match_dup 1))] + "operands[2] = gen_lowpart (QImode, operands[0]);") + +; +; zero_extendqihi2 instruction pattern(s). +; + +(define_expand "zero_extendqihi2" + [(set (match_operand:HI 0 "register_operand" "") + (zero_extend:HI (match_operand:QI 1 "register_operand" "")))] + "TARGET_ZARCH && !TARGET_EXTIMM" +{ + operands[1] = gen_lowpart (HImode, operands[1]); + emit_insn (gen_andhi3 (operands[0], operands[1], GEN_INT (0xff))); + DONE; +}) + +(define_insn "*zero_extendqihi2_64" + [(set (match_operand:HI 0 "register_operand" "=d") + (zero_extend:HI (match_operand:QI 1 "memory_operand" "RT")))] + "TARGET_ZARCH && !TARGET_EXTIMM" + "llgc\t%0,%1" + [(set_attr "op_type" "RXY") + (set_attr "z10prop" "z10_fwd_A3")]) + +(define_insn_and_split "*zero_extendqihi2_31" + [(set (match_operand:HI 0 "register_operand" "=&d") + (zero_extend:HI (match_operand:QI 1 "memory_operand" "RT")))] + "!TARGET_ZARCH" + "#" + "&& reload_completed" + [(set (match_dup 0) (const_int 0)) + (set (strict_low_part (match_dup 2)) (match_dup 1))] + "operands[2] = gen_lowpart (QImode, operands[0]);") + +; +; fixuns_trunc(dd|td)di2 instruction pattern(s). +; + +(define_expand "fixuns_truncdddi2" + [(parallel + [(set (match_operand:DI 0 "register_operand" "") + (unsigned_fix:DI (match_operand:DD 1 "register_operand" ""))) + (unspec:DI [(const_int 5)] UNSPEC_ROUND) + (clobber (reg:CC CC_REGNUM))])] + + "TARGET_HARD_DFP" +{ + if (!TARGET_Z196) + { + rtx label1 = gen_label_rtx (); + rtx label2 = gen_label_rtx (); + rtx temp = gen_reg_rtx (TDmode); + REAL_VALUE_TYPE cmp, sub; + + decimal_real_from_string (&cmp, "9223372036854775808.0"); /* 2^63 */ + decimal_real_from_string (&sub, "18446744073709551616.0"); /* 2^64 */ + + /* 2^63 can't be represented as 64bit DFP number with full precision. The + solution is doing the check and the subtraction in TD mode and using a + TD -> DI convert afterwards. */ + emit_insn (gen_extendddtd2 (temp, operands[1])); + temp = force_reg (TDmode, temp); + emit_cmp_and_jump_insns (temp, + CONST_DOUBLE_FROM_REAL_VALUE (cmp, TDmode), + LT, NULL_RTX, VOIDmode, 0, label1); + emit_insn (gen_subtd3 (temp, temp, + CONST_DOUBLE_FROM_REAL_VALUE (sub, TDmode))); + emit_insn (gen_fix_trunctddi2_dfp (operands[0], temp, GEN_INT (11))); + emit_jump (label2); + + emit_label (label1); + emit_insn (gen_fix_truncdddi2_dfp (operands[0], operands[1], GEN_INT (9))); + emit_label (label2); + DONE; + } +}) + +(define_expand "fixuns_trunctddi2" + [(parallel + [(set (match_operand:DI 0 "register_operand" "") + (unsigned_fix:DI (match_operand:TD 1 "register_operand" ""))) + (unspec:DI [(const_int 5)] UNSPEC_ROUND) + (clobber (reg:CC CC_REGNUM))])] + + "TARGET_HARD_DFP" +{ + if (!TARGET_Z196) + { + rtx label1 = gen_label_rtx (); + rtx label2 = gen_label_rtx (); + rtx temp = gen_reg_rtx (TDmode); + REAL_VALUE_TYPE cmp, sub; + + operands[1] = force_reg (TDmode, operands[1]); + decimal_real_from_string (&cmp, "9223372036854775808.0"); /* 2^63 */ + decimal_real_from_string (&sub, "18446744073709551616.0"); /* 2^64 */ + + emit_cmp_and_jump_insns (operands[1], + CONST_DOUBLE_FROM_REAL_VALUE (cmp, TDmode), + LT, NULL_RTX, VOIDmode, 0, label1); + emit_insn (gen_subtd3 (temp, operands[1], + CONST_DOUBLE_FROM_REAL_VALUE (sub, TDmode))); + emit_insn (gen_fix_trunctddi2_dfp (operands[0], temp, GEN_INT (11))); + emit_jump (label2); + + emit_label (label1); + emit_insn (gen_fix_trunctddi2_dfp (operands[0], operands[1], GEN_INT (9))); + emit_label (label2); + DONE; + } +}) + +; +; fixuns_trunc(sf|df|tf)(si|di)2 and fix_trunc(sf|df|tf)(si|di)2 +; instruction pattern(s). +; + +(define_expand "fixuns_trunc<BFP:mode><GPR:mode>2" + [(parallel + [(set (match_operand:GPR 0 "register_operand" "") + (unsigned_fix:GPR (match_operand:BFP 1 "register_operand" ""))) + (unspec:GPR [(const_int 5)] UNSPEC_ROUND) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_HARD_FLOAT" +{ + if (!TARGET_Z196) + { + rtx label1 = gen_label_rtx (); + rtx label2 = gen_label_rtx (); + rtx temp = gen_reg_rtx (<BFP:MODE>mode); + REAL_VALUE_TYPE cmp, sub; + + operands[1] = force_reg (<BFP:MODE>mode, operands[1]); + real_2expN (&cmp, GET_MODE_BITSIZE(<GPR:MODE>mode) - 1, <BFP:MODE>mode); + real_2expN (&sub, GET_MODE_BITSIZE(<GPR:MODE>mode), <BFP:MODE>mode); + + emit_cmp_and_jump_insns (operands[1], + CONST_DOUBLE_FROM_REAL_VALUE (cmp, <BFP:MODE>mode), + LT, NULL_RTX, VOIDmode, 0, label1); + emit_insn (gen_sub<BFP:mode>3 (temp, operands[1], + CONST_DOUBLE_FROM_REAL_VALUE (sub, <BFP:MODE>mode))); + emit_insn (gen_fix_trunc<BFP:mode><GPR:mode>2_bfp (operands[0], temp, + GEN_INT (7))); + emit_jump (label2); + + emit_label (label1); + emit_insn (gen_fix_trunc<BFP:mode><GPR:mode>2_bfp (operands[0], + operands[1], GEN_INT (5))); + emit_label (label2); + DONE; + } +}) + +; fixuns_trunc(td|dd)si2 expander +(define_expand "fixuns_trunc<mode>si2" + [(parallel + [(set (match_operand:SI 0 "register_operand" "") + (unsigned_fix:SI (match_operand:DFP 1 "register_operand" ""))) + (unspec:SI [(const_int 5)] UNSPEC_ROUND) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_Z196 && TARGET_HARD_DFP" + "") + +; fixuns_trunc(tf|df|sf|td|dd)(di|si)2 instruction patterns. + +; clfebr, clfdbr, clfxbr, clgebr, clgdbr, clgxbr +; clfdtr, clfxtr, clgdtr, clgxtr +(define_insn "*fixuns_trunc<FP:mode><GPR:mode>2_z196" + [(set (match_operand:GPR 0 "register_operand" "=r") + (unsigned_fix:GPR (match_operand:FP 1 "register_operand" "f"))) + (unspec:GPR [(match_operand:GPR 2 "immediate_operand" "K")] UNSPEC_ROUND) + (clobber (reg:CC CC_REGNUM))] + "TARGET_Z196" + "cl<GPR:gf><FP:xde><FP:bt>r\t%0,%h2,%1,0" + [(set_attr "op_type" "RRF") + (set_attr "type" "ftoi")]) + +(define_expand "fix_trunc<DSF:mode><GPR:mode>2" + [(set (match_operand:GPR 0 "register_operand" "") + (fix:GPR (match_operand:DSF 1 "register_operand" "")))] + "TARGET_HARD_FLOAT" +{ + emit_insn (gen_fix_trunc<DSF:mode><GPR:mode>2_bfp (operands[0], operands[1], + GEN_INT (5))); + DONE; +}) + +; cgxbr, cgdbr, cgebr, cfxbr, cfdbr, cfebr +(define_insn "fix_trunc<BFP:mode><GPR:mode>2_bfp" + [(set (match_operand:GPR 0 "register_operand" "=d") + (fix:GPR (match_operand:BFP 1 "register_operand" "f"))) + (unspec:GPR [(match_operand:GPR 2 "immediate_operand" "K")] UNSPEC_ROUND) + (clobber (reg:CC CC_REGNUM))] + "TARGET_HARD_FLOAT" + "c<GPR:gf><BFP:xde>br\t%0,%h2,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "ftoi")]) + + +; +; fix_trunc(td|dd)di2 instruction pattern(s). +; + +(define_expand "fix_trunc<mode>di2" + [(set (match_operand:DI 0 "register_operand" "") + (fix:DI (match_operand:DFP 1 "nonimmediate_operand" "")))] + "TARGET_ZARCH && TARGET_HARD_DFP" +{ + operands[1] = force_reg (<MODE>mode, operands[1]); + emit_insn (gen_fix_trunc<mode>di2_dfp (operands[0], operands[1], + GEN_INT (9))); + DONE; +}) + +; cgxtr, cgdtr +(define_insn "fix_trunc<DFP:mode>di2_dfp" + [(set (match_operand:DI 0 "register_operand" "=d") + (fix:DI (match_operand:DFP 1 "register_operand" "f"))) + (unspec:DI [(match_operand:DI 2 "immediate_operand" "K")] UNSPEC_ROUND) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH && TARGET_HARD_DFP" + "cg<DFP:xde>tr\t%0,%h2,%1" + [(set_attr "op_type" "RRF") + (set_attr "type" "ftoidfp")]) + + +; +; fix_trunctf(si|di)2 instruction pattern(s). +; + +(define_expand "fix_trunctf<mode>2" + [(parallel [(set (match_operand:GPR 0 "register_operand" "") + (fix:GPR (match_operand:TF 1 "register_operand" ""))) + (unspec:GPR [(const_int 5)] UNSPEC_ROUND) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_HARD_FLOAT" + "") + + +; +; float(si|di)(tf|df|sf|td|dd)2 instruction pattern(s). +; + +; cxgbr, cdgbr, cegbr, cxgtr, cdgtr +(define_insn "floatdi<mode>2" + [(set (match_operand:FP 0 "register_operand" "=f") + (float:FP (match_operand:DI 1 "register_operand" "d")))] + "TARGET_ZARCH && TARGET_HARD_FLOAT" + "c<xde>g<bt>r\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "itof<mode>" )]) + +; cxfbr, cdfbr, cefbr +(define_insn "floatsi<mode>2" + [(set (match_operand:BFP 0 "register_operand" "=f") + (float:BFP (match_operand:SI 1 "register_operand" "d")))] + "TARGET_HARD_FLOAT" + "c<xde>fbr\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "itof<mode>" )]) + +; cxftr, cdftr +(define_insn "floatsi<mode>2" + [(set (match_operand:DFP 0 "register_operand" "=f") + (float:DFP (match_operand:SI 1 "register_operand" "d")))] + "TARGET_Z196 && TARGET_HARD_FLOAT" + "c<xde>ftr\t%0,0,%1,0" + [(set_attr "op_type" "RRE") + (set_attr "type" "itof<mode>" )]) + +; +; floatuns(si|di)(tf|df|sf|td|dd)2 instruction pattern(s). +; + +; cxlgbr, cdlgbr, celgbr, cxlgtr, cdlgtr +; cxlfbr, cdlfbr, celfbr, cxlftr, cdlftr +(define_insn "floatuns<GPR:mode><FP:mode>2" + [(set (match_operand:FP 0 "register_operand" "=f") + (unsigned_float:FP (match_operand:GPR 1 "register_operand" "d")))] + "TARGET_Z196 && TARGET_HARD_FLOAT" + "c<FP:xde>l<GPR:gf><FP:bt>r\t%0,0,%1,0" + [(set_attr "op_type" "RRE") + (set_attr "type" "itof<FP:mode>" )]) + +; +; truncdfsf2 instruction pattern(s). +; + +(define_insn "truncdfsf2" + [(set (match_operand:SF 0 "register_operand" "=f") + (float_truncate:SF (match_operand:DF 1 "register_operand" "f")))] + "TARGET_HARD_FLOAT" + "ledbr\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "ftruncdf")]) + +; +; trunctf(df|sf)2 instruction pattern(s). +; + +; ldxbr, lexbr +(define_insn "trunctf<mode>2" + [(set (match_operand:DSF 0 "register_operand" "=f") + (float_truncate:DSF (match_operand:TF 1 "register_operand" "f"))) + (clobber (match_scratch:TF 2 "=f"))] + "TARGET_HARD_FLOAT" + "l<xde>xbr\t%2,%1\;l<xde>r\t%0,%2" + [(set_attr "length" "6") + (set_attr "type" "ftrunctf")]) + +; +; trunctddd2 and truncddsd2 instruction pattern(s). +; + +(define_insn "trunctddd2" + [(set (match_operand:DD 0 "register_operand" "=f") + (float_truncate:DD (match_operand:TD 1 "register_operand" "f"))) + (clobber (match_scratch:TD 2 "=f"))] + "TARGET_HARD_DFP" + "ldxtr\t%2,0,%1,0\;ldr\t%0,%2" + [(set_attr "length" "6") + (set_attr "type" "ftruncdd")]) + +(define_insn "truncddsd2" + [(set (match_operand:SD 0 "register_operand" "=f") + (float_truncate:SD (match_operand:DD 1 "register_operand" "f")))] + "TARGET_HARD_DFP" + "ledtr\t%0,0,%1,0" + [(set_attr "op_type" "RRF") + (set_attr "type" "ftruncsd")]) + +; +; extend(sf|df)(df|tf)2 instruction pattern(s). +; + +; ldebr, ldeb, lxdbr, lxdb, lxebr, lxeb +(define_insn "extend<DSF:mode><BFP:mode>2" + [(set (match_operand:BFP 0 "register_operand" "=f,f") + (float_extend:BFP (match_operand:DSF 1 "nonimmediate_operand" "f,R")))] + "TARGET_HARD_FLOAT + && GET_MODE_SIZE (<BFP:MODE>mode) > GET_MODE_SIZE (<DSF:MODE>mode)" + "@ + l<BFP:xde><DSF:xde>br\t%0,%1 + l<BFP:xde><DSF:xde>b\t%0,%1" + [(set_attr "op_type" "RRE,RXE") + (set_attr "type" "fsimp<BFP:mode>, fload<BFP:mode>")]) + +; +; extendddtd2 and extendsddd2 instruction pattern(s). +; + +(define_insn "extendddtd2" + [(set (match_operand:TD 0 "register_operand" "=f") + (float_extend:TD (match_operand:DD 1 "register_operand" "f")))] + "TARGET_HARD_DFP" + "lxdtr\t%0,%1,0" + [(set_attr "op_type" "RRF") + (set_attr "type" "fsimptf")]) + +(define_insn "extendsddd2" + [(set (match_operand:DD 0 "register_operand" "=f") + (float_extend:DD (match_operand:SD 1 "register_operand" "f")))] + "TARGET_HARD_DFP" + "ldetr\t%0,%1,0" + [(set_attr "op_type" "RRF") + (set_attr "type" "fsimptf")]) + +; Binary <-> Decimal floating point trunc patterns +; + +(define_insn "*trunc<BFP:mode><DFP_ALL:mode>2" + [(set (reg:DFP_ALL FPR0_REGNUM) + (float_truncate:DFP_ALL (reg:BFP FPR2_REGNUM))) + (use (reg:SI GPR0_REGNUM)) + (clobber (reg:CC CC_REGNUM))] + "TARGET_HARD_DFP" + "pfpo") + +(define_insn "*trunc<DFP_ALL:mode><BFP:mode>2" + [(set (reg:BFP FPR0_REGNUM) + (float_truncate:BFP (reg:DFP_ALL FPR2_REGNUM))) + (use (reg:SI GPR0_REGNUM)) + (clobber (reg:CC CC_REGNUM))] + "TARGET_HARD_DFP" + "pfpo") + +(define_expand "trunc<BFP:mode><DFP_ALL:mode>2" + [(set (reg:BFP FPR2_REGNUM) (match_operand:BFP 1 "nonimmediate_operand" "")) + (set (reg:SI GPR0_REGNUM) (match_dup 2)) + (parallel + [(set (reg:DFP_ALL FPR0_REGNUM) + (float_truncate:DFP_ALL (reg:BFP FPR2_REGNUM))) + (use (reg:SI GPR0_REGNUM)) + (clobber (reg:CC CC_REGNUM))]) + (set (match_operand:DFP_ALL 0 "nonimmediate_operand" "") + (reg:DFP_ALL FPR0_REGNUM))] + "TARGET_HARD_DFP + && GET_MODE_SIZE (<BFP:MODE>mode) > GET_MODE_SIZE (<DFP_ALL:MODE>mode)" +{ + HOST_WIDE_INT flags; + + flags = (PFPO_CONVERT | + PFPO_OP_TYPE_<DFP_ALL:MODE> << PFPO_OP0_TYPE_SHIFT | + PFPO_OP_TYPE_<BFP:MODE> << PFPO_OP1_TYPE_SHIFT); + + operands[2] = GEN_INT (flags); +}) + +(define_expand "trunc<DFP_ALL:mode><BFP:mode>2" + [(set (reg:DFP_ALL FPR2_REGNUM) + (match_operand:DFP_ALL 1 "nonimmediate_operand" "")) + (set (reg:SI GPR0_REGNUM) (match_dup 2)) + (parallel + [(set (reg:BFP FPR0_REGNUM) (float_truncate:BFP (reg:DFP_ALL FPR2_REGNUM))) + (use (reg:SI GPR0_REGNUM)) + (clobber (reg:CC CC_REGNUM))]) + (set (match_operand:BFP 0 "nonimmediate_operand" "") (reg:BFP FPR0_REGNUM))] + "TARGET_HARD_DFP + && GET_MODE_SIZE (<DFP_ALL:MODE>mode) >= GET_MODE_SIZE (<BFP:MODE>mode)" +{ + HOST_WIDE_INT flags; + + flags = (PFPO_CONVERT | + PFPO_OP_TYPE_<BFP:MODE> << PFPO_OP0_TYPE_SHIFT | + PFPO_OP_TYPE_<DFP_ALL:MODE> << PFPO_OP1_TYPE_SHIFT); + + operands[2] = GEN_INT (flags); +}) + +; +; Binary <-> Decimal floating point extend patterns +; + +(define_insn "*extend<BFP:mode><DFP_ALL:mode>2" + [(set (reg:DFP_ALL FPR0_REGNUM) (float_extend:DFP_ALL (reg:BFP FPR2_REGNUM))) + (use (reg:SI GPR0_REGNUM)) + (clobber (reg:CC CC_REGNUM))] + "TARGET_HARD_DFP" + "pfpo") + +(define_insn "*extend<DFP_ALL:mode><BFP:mode>2" + [(set (reg:BFP FPR0_REGNUM) (float_extend:BFP (reg:DFP_ALL FPR2_REGNUM))) + (use (reg:SI GPR0_REGNUM)) + (clobber (reg:CC CC_REGNUM))] + "TARGET_HARD_DFP" + "pfpo") + +(define_expand "extend<BFP:mode><DFP_ALL:mode>2" + [(set (reg:BFP FPR2_REGNUM) (match_operand:BFP 1 "nonimmediate_operand" "")) + (set (reg:SI GPR0_REGNUM) (match_dup 2)) + (parallel + [(set (reg:DFP_ALL FPR0_REGNUM) + (float_extend:DFP_ALL (reg:BFP FPR2_REGNUM))) + (use (reg:SI GPR0_REGNUM)) + (clobber (reg:CC CC_REGNUM))]) + (set (match_operand:DFP_ALL 0 "nonimmediate_operand" "") + (reg:DFP_ALL FPR0_REGNUM))] + "TARGET_HARD_DFP + && GET_MODE_SIZE (<BFP:MODE>mode) <= GET_MODE_SIZE (<DFP_ALL:MODE>mode)" +{ + HOST_WIDE_INT flags; + + flags = (PFPO_CONVERT | + PFPO_OP_TYPE_<DFP_ALL:MODE> << PFPO_OP0_TYPE_SHIFT | + PFPO_OP_TYPE_<BFP:MODE> << PFPO_OP1_TYPE_SHIFT); + + operands[2] = GEN_INT (flags); +}) + +(define_expand "extend<DFP_ALL:mode><BFP:mode>2" + [(set (reg:DFP_ALL FPR2_REGNUM) + (match_operand:DFP_ALL 1 "nonimmediate_operand" "")) + (set (reg:SI GPR0_REGNUM) (match_dup 2)) + (parallel + [(set (reg:BFP FPR0_REGNUM) (float_extend:BFP (reg:DFP_ALL FPR2_REGNUM))) + (use (reg:SI GPR0_REGNUM)) + (clobber (reg:CC CC_REGNUM))]) + (set (match_operand:BFP 0 "nonimmediate_operand" "") (reg:BFP FPR0_REGNUM))] + "TARGET_HARD_DFP + && GET_MODE_SIZE (<DFP_ALL:MODE>mode) < GET_MODE_SIZE (<BFP:MODE>mode)" +{ + HOST_WIDE_INT flags; + + flags = (PFPO_CONVERT | + PFPO_OP_TYPE_<BFP:MODE> << PFPO_OP0_TYPE_SHIFT | + PFPO_OP_TYPE_<DFP_ALL:MODE> << PFPO_OP1_TYPE_SHIFT); + + operands[2] = GEN_INT (flags); +}) + + +;; +;; ARITHMETIC OPERATIONS +;; +; arithmetic operations set the ConditionCode, +; because of unpredictable Bits in Register for Halfword and Byte +; the ConditionCode can be set wrong in operations for Halfword and Byte + +;; +;;- Add instructions. +;; + +; +; addti3 instruction pattern(s). +; + +(define_insn_and_split "addti3" + [(set (match_operand:TI 0 "register_operand" "=&d") + (plus:TI (match_operand:TI 1 "nonimmediate_operand" "%0") + (match_operand:TI 2 "general_operand" "do") ) ) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" + "#" + "&& reload_completed" + [(parallel + [(set (reg:CCL1 CC_REGNUM) + (compare:CCL1 (plus:DI (match_dup 7) (match_dup 8)) + (match_dup 7))) + (set (match_dup 6) (plus:DI (match_dup 7) (match_dup 8)))]) + (parallel + [(set (match_dup 3) (plus:DI + (plus:DI (ltu:DI (reg:CCL1 CC_REGNUM) (const_int 0)) + (match_dup 4)) (match_dup 5))) + (clobber (reg:CC CC_REGNUM))])] + "operands[3] = operand_subword (operands[0], 0, 0, TImode); + operands[4] = operand_subword (operands[1], 0, 0, TImode); + operands[5] = operand_subword (operands[2], 0, 0, TImode); + operands[6] = operand_subword (operands[0], 1, 0, TImode); + operands[7] = operand_subword (operands[1], 1, 0, TImode); + operands[8] = operand_subword (operands[2], 1, 0, TImode);") + +; +; adddi3 instruction pattern(s). +; + +(define_expand "adddi3" + [(parallel + [(set (match_operand:DI 0 "nonimmediate_operand" "") + (plus:DI (match_operand:DI 1 "nonimmediate_operand" "") + (match_operand:DI 2 "general_operand" ""))) + (clobber (reg:CC CC_REGNUM))])] + "" + "") + +(define_insn "*adddi3_sign" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (plus:DI (sign_extend:DI (match_operand:SI 2 "general_operand" "d,RT")) + (match_operand:DI 1 "register_operand" "0,0"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" + "@ + agfr\t%0,%2 + agf\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z196prop" "z196_cracked,z196_cracked")]) + +(define_insn "*adddi3_zero_cc" + [(set (reg CC_REGNUM) + (compare (plus:DI (zero_extend:DI (match_operand:SI 2 "general_operand" "d,RT")) + (match_operand:DI 1 "register_operand" "0,0")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d,d") + (plus:DI (zero_extend:DI (match_dup 2)) (match_dup 1)))] + "s390_match_ccmode (insn, CCLmode) && TARGET_ZARCH" + "@ + algfr\t%0,%2 + algf\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + +(define_insn "*adddi3_zero_cconly" + [(set (reg CC_REGNUM) + (compare (plus:DI (zero_extend:DI (match_operand:SI 2 "general_operand" "d,RT")) + (match_operand:DI 1 "register_operand" "0,0")) + (const_int 0))) + (clobber (match_scratch:DI 0 "=d,d"))] + "s390_match_ccmode (insn, CCLmode) && TARGET_ZARCH" + "@ + algfr\t%0,%2 + algf\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + +(define_insn "*adddi3_zero" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (plus:DI (zero_extend:DI (match_operand:SI 2 "general_operand" "d,RT")) + (match_operand:DI 1 "register_operand" "0,0"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" + "@ + algfr\t%0,%2 + algf\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_super_E1,z10_super_E1")]) + +(define_insn_and_split "*adddi3_31z" + [(set (match_operand:DI 0 "nonimmediate_operand" "=&d") + (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0") + (match_operand:DI 2 "general_operand" "do") ) ) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH && TARGET_CPU_ZARCH" + "#" + "&& reload_completed" + [(parallel + [(set (reg:CCL1 CC_REGNUM) + (compare:CCL1 (plus:SI (match_dup 7) (match_dup 8)) + (match_dup 7))) + (set (match_dup 6) (plus:SI (match_dup 7) (match_dup 8)))]) + (parallel + [(set (match_dup 3) (plus:SI + (plus:SI (ltu:SI (reg:CCL1 CC_REGNUM) (const_int 0)) + (match_dup 4)) (match_dup 5))) + (clobber (reg:CC CC_REGNUM))])] + "operands[3] = operand_subword (operands[0], 0, 0, DImode); + operands[4] = operand_subword (operands[1], 0, 0, DImode); + operands[5] = operand_subword (operands[2], 0, 0, DImode); + operands[6] = operand_subword (operands[0], 1, 0, DImode); + operands[7] = operand_subword (operands[1], 1, 0, DImode); + operands[8] = operand_subword (operands[2], 1, 0, DImode);") + +(define_insn_and_split "*adddi3_31" + [(set (match_operand:DI 0 "nonimmediate_operand" "=&d") + (plus:DI (match_operand:DI 1 "nonimmediate_operand" "%0") + (match_operand:DI 2 "general_operand" "do") ) ) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_CPU_ZARCH" + "#" + "&& reload_completed" + [(parallel + [(set (match_dup 3) (plus:SI (match_dup 4) (match_dup 5))) + (clobber (reg:CC CC_REGNUM))]) + (parallel + [(set (reg:CCL1 CC_REGNUM) + (compare:CCL1 (plus:SI (match_dup 7) (match_dup 8)) + (match_dup 7))) + (set (match_dup 6) (plus:SI (match_dup 7) (match_dup 8)))]) + (set (pc) + (if_then_else (ltu (reg:CCL1 CC_REGNUM) (const_int 0)) + (pc) + (label_ref (match_dup 9)))) + (parallel + [(set (match_dup 3) (plus:SI (match_dup 3) (const_int 1))) + (clobber (reg:CC CC_REGNUM))]) + (match_dup 9)] + "operands[3] = operand_subword (operands[0], 0, 0, DImode); + operands[4] = operand_subword (operands[1], 0, 0, DImode); + operands[5] = operand_subword (operands[2], 0, 0, DImode); + operands[6] = operand_subword (operands[0], 1, 0, DImode); + operands[7] = operand_subword (operands[1], 1, 0, DImode); + operands[8] = operand_subword (operands[2], 1, 0, DImode); + operands[9] = gen_label_rtx ();") + +; +; addsi3 instruction pattern(s). +; + +(define_expand "addsi3" + [(parallel + [(set (match_operand:SI 0 "nonimmediate_operand" "") + (plus:SI (match_operand:SI 1 "nonimmediate_operand" "") + (match_operand:SI 2 "general_operand" ""))) + (clobber (reg:CC CC_REGNUM))])] + "" + "") + +(define_insn "*addsi3_sign" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (plus:SI (sign_extend:SI (match_operand:HI 2 "memory_operand" "R,T")) + (match_operand:SI 1 "register_operand" "0,0"))) + (clobber (reg:CC CC_REGNUM))] + "" + "@ + ah\t%0,%2 + ahy\t%0,%2" + [(set_attr "op_type" "RX,RXY") + (set_attr "z196prop" "z196_cracked,z196_cracked")]) + +; +; add(di|si)3 instruction pattern(s). +; + +; ark, agrk, ar, ahi, ahik, aghik, alfi, slfi, a, ay, agr, aghi, algfi, slgfi, ag, asi, agsi +(define_insn "*add<mode>3" + [(set (match_operand:GPR 0 "nonimmediate_operand" "=d,d,d,d, d, d,d,d,QS") + (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,d,0,d, 0, 0,0,0, 0") + (match_operand:GPR 2 "general_operand" " d,d,K,K,Op,On,R,T, C") ) ) + (clobber (reg:CC CC_REGNUM))] + "" + "@ + a<g>r\t%0,%2 + a<g>rk\t%0,%1,%2 + a<g>hi\t%0,%h2 + a<g>hik\t%0,%1,%h2 + al<g>fi\t%0,%2 + sl<g>fi\t%0,%n2 + a<g>\t%0,%2 + a<y>\t%0,%2 + a<g>si\t%0,%c2" + [(set_attr "op_type" "RR<E>,RRF,RI,RIE,RIL,RIL,RX<Y>,RXY,SIY") + (set_attr "cpu_facility" "*,z196,*,z196,extimm,extimm,*,*,z10") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1,*,z10_super_E1,z10_super_E1, + z10_super_E1,z10_super_E1,z10_super_E1")]) + +; alr, alfi, slfi, al, aly, alrk, alhsik, algr, algfi, slgfi, alg, alsi, algsi, algrk, alghsik +(define_insn "*add<mode>3_carry1_cc" + [(set (reg CC_REGNUM) + (compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,d, 0, 0,d,0,0,0") + (match_operand:GPR 2 "general_operand" " d,d,Op,On,K,R,T,C")) + (match_dup 1))) + (set (match_operand:GPR 0 "nonimmediate_operand" "=d,d, d, d,d,d,d,d") + (plus:GPR (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCL1mode)" + "@ + al<g>r\t%0,%2 + al<g>rk\t%0,%1,%2 + al<g>fi\t%0,%2 + sl<g>fi\t%0,%n2 + al<g>hsik\t%0,%1,%h2 + al<g>\t%0,%2 + al<y>\t%0,%2 + al<g>si\t%0,%c2" + [(set_attr "op_type" "RR<E>,RRF,RIL,RIL,RIE,RX<Y>,RXY,SIY") + (set_attr "cpu_facility" "*,z196,extimm,extimm,z196,*,*,z10") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1,z10_super_E1,*, + z10_super_E1,z10_super_E1,z10_super_E1")]) + +; alr, al, aly, algr, alg, alrk, algrk +(define_insn "*add<mode>3_carry1_cconly" + [(set (reg CC_REGNUM) + (compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,d,0,0") + (match_operand:GPR 2 "general_operand" "d,d,R,T")) + (match_dup 1))) + (clobber (match_scratch:GPR 0 "=d,d,d,d"))] + "s390_match_ccmode (insn, CCL1mode)" + "@ + al<g>r\t%0,%2 + al<g>rk\t%0,%1,%2 + al<g>\t%0,%2 + al<y>\t%0,%2" + [(set_attr "op_type" "RR<E>,RRF,RX<Y>,RXY") + (set_attr "cpu_facility" "*,z196,*,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1,z10_super_E1")]) + +; alr, alfi, slfi, al, aly, algr, algfi, slgfi, alg, alsi, algsi, alrk, algrk, alhsik, alghsik +(define_insn "*add<mode>3_carry2_cc" + [(set (reg CC_REGNUM) + (compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,d, 0, 0,d,0,0, 0") + (match_operand:GPR 2 "general_operand" " d,d,Op,On,K,R,T, C")) + (match_dup 2))) + (set (match_operand:GPR 0 "nonimmediate_operand" "=d,d, d, d,d,d,d,RS") + (plus:GPR (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCL1mode)" + "@ + al<g>r\t%0,%2 + al<g>rk\t%0,%1,%2 + al<g>fi\t%0,%2 + sl<g>fi\t%0,%n2 + al<g>hsik\t%0,%1,%h2 + al<g>\t%0,%2 + al<y>\t%0,%2 + al<g>si\t%0,%c2" + [(set_attr "op_type" "RR<E>,RRF,RIL,RIL,RIE,RX<Y>,RXY,SIY") + (set_attr "cpu_facility" "*,z196,extimm,extimm,z196,*,*,z10") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1,z10_super_E1,*, + z10_super_E1,z10_super_E1,z10_super_E1")]) + +; alr, al, aly, algr, alg, alrk, algrk +(define_insn "*add<mode>3_carry2_cconly" + [(set (reg CC_REGNUM) + (compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,d,0,0") + (match_operand:GPR 2 "general_operand" "d,d,R,T")) + (match_dup 2))) + (clobber (match_scratch:GPR 0 "=d,d,d,d"))] + "s390_match_ccmode (insn, CCL1mode)" + "@ + al<g>r\t%0,%2 + al<g>rk\t%0,%1,%2 + al<g>\t%0,%2 + al<y>\t%0,%2" + [(set_attr "op_type" "RR<E>,RRF,RX<Y>,RXY") + (set_attr "cpu_facility" "*,z196,*,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1,z10_super_E1")]) + +; alr, alfi, slfi, al, aly, algr, algfi, slgfi, alg, alsi, algsi, alrk, algrk, alhsik, alghsik +(define_insn "*add<mode>3_cc" + [(set (reg CC_REGNUM) + (compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,d, 0, 0,d,0,0, 0") + (match_operand:GPR 2 "general_operand" " d,d,Op,On,K,R,T, C")) + (const_int 0))) + (set (match_operand:GPR 0 "nonimmediate_operand" "=d,d, d, d,d,d,d,RS") + (plus:GPR (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCLmode)" + "@ + al<g>r\t%0,%2 + al<g>rk\t%0,%1,%2 + al<g>fi\t%0,%2 + sl<g>fi\t%0,%n2 + al<g>hsik\t%0,%1,%h2 + al<g>\t%0,%2 + al<y>\t%0,%2 + al<g>si\t%0,%c2" + [(set_attr "op_type" "RR<E>,RRF,RIL,RIL,RIE,RX<Y>,RXY,SIY") + (set_attr "cpu_facility" "*,z196,extimm,extimm,z196,*,*,z10") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1,z10_super_E1, + *,z10_super_E1,z10_super_E1,z10_super_E1")]) + +; alr, al, aly, algr, alg, alrk, algrk +(define_insn "*add<mode>3_cconly" + [(set (reg CC_REGNUM) + (compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,d,0,0") + (match_operand:GPR 2 "general_operand" "d,d,R,T")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=d,d,d,d"))] + "s390_match_ccmode (insn, CCLmode)" + "@ + al<g>r\t%0,%2 + al<g>rk\t%0,%1,%2 + al<g>\t%0,%2 + al<y>\t%0,%2" + [(set_attr "op_type" "RR<E>,RRF,RX<Y>,RXY") + (set_attr "cpu_facility" "*,z196,*,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1,z10_super_E1")]) + +; alr, al, aly, algr, alg, alrk, algrk +(define_insn "*add<mode>3_cconly2" + [(set (reg CC_REGNUM) + (compare (match_operand:GPR 1 "nonimmediate_operand" "%0,d,0,0") + (neg:GPR (match_operand:GPR 2 "general_operand" "d,d,R,T")))) + (clobber (match_scratch:GPR 0 "=d,d,d,d"))] + "s390_match_ccmode(insn, CCLmode)" + "@ + al<g>r\t%0,%2 + al<g>rk\t%0,%1,%2 + al<g>\t%0,%2 + al<y>\t%0,%2" + [(set_attr "op_type" "RR<E>,RRF,RX<Y>,RXY") + (set_attr "cpu_facility" "*,z196,*,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1,z10_super_E1")]) + +; ahi, afi, aghi, agfi, asi, agsi +(define_insn "*add<mode>3_imm_cc" + [(set (reg CC_REGNUM) + (compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" " 0, d,0, 0") + (match_operand:GPR 2 "const_int_operand" " K, K,Os, C")) + (const_int 0))) + (set (match_operand:GPR 0 "nonimmediate_operand" "=d, d,d,QS") + (plus:GPR (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCAmode) + && (CONST_OK_FOR_CONSTRAINT_P (INTVAL (operands[2]), 'K', \"K\") + || CONST_OK_FOR_CONSTRAINT_P (INTVAL (operands[2]), 'O', \"Os\") + || CONST_OK_FOR_CONSTRAINT_P (INTVAL (operands[2]), 'C', \"C\")) + && INTVAL (operands[2]) != -((HOST_WIDE_INT)1 << (GET_MODE_BITSIZE(<MODE>mode) - 1))" + "@ + a<g>hi\t%0,%h2 + a<g>hik\t%0,%1,%h2 + a<g>fi\t%0,%2 + a<g>si\t%0,%c2" + [(set_attr "op_type" "RI,RIE,RIL,SIY") + (set_attr "cpu_facility" "*,z196,extimm,z10") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1,z10_super_E1")]) + +; +; add(tf|df|sf|td|dd)3 instruction pattern(s). +; + +; axbr, adbr, aebr, axb, adb, aeb, adtr, axtr +(define_insn "add<mode>3" + [(set (match_operand:FP 0 "register_operand" "=f, f") + (plus:FP (match_operand:FP 1 "nonimmediate_operand" "%<f0>,0") + (match_operand:FP 2 "general_operand" " f,<Rf>"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_HARD_FLOAT" + "@ + a<xde><bt>r\t%0,<op1>%2 + a<xde>b\t%0,%2" + [(set_attr "op_type" "<RRer>,RXE") + (set_attr "type" "fsimp<mode>")]) + +; axbr, adbr, aebr, axb, adb, aeb, adtr, axtr +(define_insn "*add<mode>3_cc" + [(set (reg CC_REGNUM) + (compare (plus:FP (match_operand:FP 1 "nonimmediate_operand" "%<f0>,0") + (match_operand:FP 2 "general_operand" " f,<Rf>")) + (match_operand:FP 3 "const0_operand" ""))) + (set (match_operand:FP 0 "register_operand" "=f,f") + (plus:FP (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT" + "@ + a<xde><bt>r\t%0,<op1>%2 + a<xde>b\t%0,%2" + [(set_attr "op_type" "<RRer>,RXE") + (set_attr "type" "fsimp<mode>")]) + +; axbr, adbr, aebr, axb, adb, aeb, adtr, axtr +(define_insn "*add<mode>3_cconly" + [(set (reg CC_REGNUM) + (compare (plus:FP (match_operand:FP 1 "nonimmediate_operand" "%<f0>,0") + (match_operand:FP 2 "general_operand" " f,<Rf>")) + (match_operand:FP 3 "const0_operand" ""))) + (clobber (match_scratch:FP 0 "=f,f"))] + "s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT" + "@ + a<xde><bt>r\t%0,<op1>%2 + a<xde>b\t%0,%2" + [(set_attr "op_type" "<RRer>,RXE") + (set_attr "type" "fsimp<mode>")]) + + +;; +;;- Subtract instructions. +;; + +; +; subti3 instruction pattern(s). +; + +(define_insn_and_split "subti3" + [(set (match_operand:TI 0 "register_operand" "=&d") + (minus:TI (match_operand:TI 1 "register_operand" "0") + (match_operand:TI 2 "general_operand" "do") ) ) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" + "#" + "&& reload_completed" + [(parallel + [(set (reg:CCL2 CC_REGNUM) + (compare:CCL2 (minus:DI (match_dup 7) (match_dup 8)) + (match_dup 7))) + (set (match_dup 6) (minus:DI (match_dup 7) (match_dup 8)))]) + (parallel + [(set (match_dup 3) (minus:DI (minus:DI (match_dup 4) (match_dup 5)) + (gtu:DI (reg:CCL2 CC_REGNUM) (const_int 0)))) + (clobber (reg:CC CC_REGNUM))])] + "operands[3] = operand_subword (operands[0], 0, 0, TImode); + operands[4] = operand_subword (operands[1], 0, 0, TImode); + operands[5] = operand_subword (operands[2], 0, 0, TImode); + operands[6] = operand_subword (operands[0], 1, 0, TImode); + operands[7] = operand_subword (operands[1], 1, 0, TImode); + operands[8] = operand_subword (operands[2], 1, 0, TImode);") + +; +; subdi3 instruction pattern(s). +; + +(define_expand "subdi3" + [(parallel + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "general_operand" ""))) + (clobber (reg:CC CC_REGNUM))])] + "" + "") + +(define_insn "*subdi3_sign" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (minus:DI (match_operand:DI 1 "register_operand" "0,0") + (sign_extend:DI (match_operand:SI 2 "general_operand" "d,RT")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" + "@ + sgfr\t%0,%2 + sgf\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_c,*") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "*subdi3_zero_cc" + [(set (reg CC_REGNUM) + (compare (minus:DI (match_operand:DI 1 "register_operand" "0,0") + (zero_extend:DI (match_operand:SI 2 "general_operand" "d,RT"))) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d,d") + (minus:DI (match_dup 1) (zero_extend:DI (match_dup 2))))] + "s390_match_ccmode (insn, CCLmode) && TARGET_ZARCH" + "@ + slgfr\t%0,%2 + slgf\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_super_c_E1,z10_super_E1")]) + +(define_insn "*subdi3_zero_cconly" + [(set (reg CC_REGNUM) + (compare (minus:DI (match_operand:DI 1 "register_operand" "0,0") + (zero_extend:DI (match_operand:SI 2 "general_operand" "d,RT"))) + (const_int 0))) + (clobber (match_scratch:DI 0 "=d,d"))] + "s390_match_ccmode (insn, CCLmode) && TARGET_ZARCH" + "@ + slgfr\t%0,%2 + slgf\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_super_c_E1,z10_super_E1")]) + +(define_insn "*subdi3_zero" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (minus:DI (match_operand:DI 1 "register_operand" "0,0") + (zero_extend:DI (match_operand:SI 2 "general_operand" "d,RT")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" + "@ + slgfr\t%0,%2 + slgf\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_super_c_E1,z10_super_E1")]) + +(define_insn_and_split "*subdi3_31z" + [(set (match_operand:DI 0 "register_operand" "=&d") + (minus:DI (match_operand:DI 1 "register_operand" "0") + (match_operand:DI 2 "general_operand" "do") ) ) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH && TARGET_CPU_ZARCH" + "#" + "&& reload_completed" + [(parallel + [(set (reg:CCL2 CC_REGNUM) + (compare:CCL2 (minus:SI (match_dup 7) (match_dup 8)) + (match_dup 7))) + (set (match_dup 6) (minus:SI (match_dup 7) (match_dup 8)))]) + (parallel + [(set (match_dup 3) (minus:SI (minus:SI (match_dup 4) (match_dup 5)) + (gtu:SI (reg:CCL2 CC_REGNUM) (const_int 0)))) + (clobber (reg:CC CC_REGNUM))])] + "operands[3] = operand_subword (operands[0], 0, 0, DImode); + operands[4] = operand_subword (operands[1], 0, 0, DImode); + operands[5] = operand_subword (operands[2], 0, 0, DImode); + operands[6] = operand_subword (operands[0], 1, 0, DImode); + operands[7] = operand_subword (operands[1], 1, 0, DImode); + operands[8] = operand_subword (operands[2], 1, 0, DImode);") + +(define_insn_and_split "*subdi3_31" + [(set (match_operand:DI 0 "register_operand" "=&d") + (minus:DI (match_operand:DI 1 "register_operand" "0") + (match_operand:DI 2 "general_operand" "do") ) ) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_CPU_ZARCH" + "#" + "&& reload_completed" + [(parallel + [(set (match_dup 3) (minus:SI (match_dup 4) (match_dup 5))) + (clobber (reg:CC CC_REGNUM))]) + (parallel + [(set (reg:CCL2 CC_REGNUM) + (compare:CCL2 (minus:SI (match_dup 7) (match_dup 8)) + (match_dup 7))) + (set (match_dup 6) (minus:SI (match_dup 7) (match_dup 8)))]) + (set (pc) + (if_then_else (gtu (reg:CCL2 CC_REGNUM) (const_int 0)) + (pc) + (label_ref (match_dup 9)))) + (parallel + [(set (match_dup 3) (plus:SI (match_dup 3) (const_int -1))) + (clobber (reg:CC CC_REGNUM))]) + (match_dup 9)] + "operands[3] = operand_subword (operands[0], 0, 0, DImode); + operands[4] = operand_subword (operands[1], 0, 0, DImode); + operands[5] = operand_subword (operands[2], 0, 0, DImode); + operands[6] = operand_subword (operands[0], 1, 0, DImode); + operands[7] = operand_subword (operands[1], 1, 0, DImode); + operands[8] = operand_subword (operands[2], 1, 0, DImode); + operands[9] = gen_label_rtx ();") + +; +; subsi3 instruction pattern(s). +; + +(define_expand "subsi3" + [(parallel + [(set (match_operand:SI 0 "register_operand" "") + (minus:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "general_operand" ""))) + (clobber (reg:CC CC_REGNUM))])] + "" + "") + +(define_insn "*subsi3_sign" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (minus:SI (match_operand:SI 1 "register_operand" "0,0") + (sign_extend:SI (match_operand:HI 2 "memory_operand" "R,T")))) + (clobber (reg:CC CC_REGNUM))] + "" + "@ + sh\t%0,%2 + shy\t%0,%2" + [(set_attr "op_type" "RX,RXY") + (set_attr "z196prop" "z196_cracked,z196_cracked")]) + +; +; sub(di|si)3 instruction pattern(s). +; + +; sr, s, sy, sgr, sg, srk, sgrk +(define_insn "*sub<mode>3" + [(set (match_operand:GPR 0 "register_operand" "=d,d,d,d") + (minus:GPR (match_operand:GPR 1 "register_operand" "0,d,0,0") + (match_operand:GPR 2 "general_operand" "d,d,R,T") ) ) + (clobber (reg:CC CC_REGNUM))] + "" + "@ + s<g>r\t%0,%2 + s<g>rk\t%0,%1,%2 + s<g>\t%0,%2 + s<y>\t%0,%2" + [(set_attr "op_type" "RR<E>,RRF,RX<Y>,RXY") + (set_attr "cpu_facility" "*,z196,*,*") + (set_attr "z10prop" "z10_super_c_E1,*,z10_super_E1,z10_super_E1")]) + +; slr, sl, sly, slgr, slg, slrk, slgrk +(define_insn "*sub<mode>3_borrow_cc" + [(set (reg CC_REGNUM) + (compare (minus:GPR (match_operand:GPR 1 "register_operand" "0,d,0,0") + (match_operand:GPR 2 "general_operand" "d,d,R,T")) + (match_dup 1))) + (set (match_operand:GPR 0 "register_operand" "=d,d,d,d") + (minus:GPR (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCL2mode)" + "@ + sl<g>r\t%0,%2 + sl<g>rk\t%0,%1,%2 + sl<g>\t%0,%2 + sl<y>\t%0,%2" + [(set_attr "op_type" "RR<E>,RRF,RX<Y>,RXY") + (set_attr "cpu_facility" "*,z196,*,*") + (set_attr "z10prop" "z10_super_c_E1,*,z10_super_E1,z10_super_E1")]) + +; slr, sl, sly, slgr, slg, slrk, slgrk +(define_insn "*sub<mode>3_borrow_cconly" + [(set (reg CC_REGNUM) + (compare (minus:GPR (match_operand:GPR 1 "register_operand" "0,d,0,0") + (match_operand:GPR 2 "general_operand" "d,d,R,T")) + (match_dup 1))) + (clobber (match_scratch:GPR 0 "=d,d,d,d"))] + "s390_match_ccmode (insn, CCL2mode)" + "@ + sl<g>r\t%0,%2 + sl<g>rk\t%0,%1,%2 + sl<g>\t%0,%2 + sl<y>\t%0,%2" + [(set_attr "op_type" "RR<E>,RRF,RX<Y>,RXY") + (set_attr "cpu_facility" "*,z196,*,*") + (set_attr "z10prop" "z10_super_c_E1,*,z10_super_E1,z10_super_E1")]) + +; slr, sl, sly, slgr, slg, slrk, slgrk +(define_insn "*sub<mode>3_cc" + [(set (reg CC_REGNUM) + (compare (minus:GPR (match_operand:GPR 1 "register_operand" "0,d,0,0") + (match_operand:GPR 2 "general_operand" "d,d,R,T")) + (const_int 0))) + (set (match_operand:GPR 0 "register_operand" "=d,d,d,d") + (minus:GPR (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCLmode)" + "@ + sl<g>r\t%0,%2 + sl<g>rk\t%0,%1,%2 + sl<g>\t%0,%2 + sl<y>\t%0,%2" + [(set_attr "op_type" "RR<E>,RRF,RX<Y>,RXY") + (set_attr "cpu_facility" "*,z196,*,*") + (set_attr "z10prop" "z10_super_c_E1,*,z10_super_E1,z10_super_E1")]) + +; slr, sl, sly, slgr, slg, slrk, slgrk +(define_insn "*sub<mode>3_cc2" + [(set (reg CC_REGNUM) + (compare (match_operand:GPR 1 "register_operand" "0,d,0,0") + (match_operand:GPR 2 "general_operand" "d,d,R,T"))) + (set (match_operand:GPR 0 "register_operand" "=d,d,d,d") + (minus:GPR (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCL3mode)" + "@ + sl<g>r\t%0,%2 + sl<g>rk\t%0,%1,%2 + sl<g>\t%0,%2 + sl<y>\t%0,%2" + [(set_attr "op_type" "RR<E>,RRF,RX<Y>,RXY") + (set_attr "cpu_facility" "*,z196,*,*") + (set_attr "z10prop" "z10_super_c_E1,*,z10_super_E1,z10_super_E1")]) + +; slr, sl, sly, slgr, slg, slrk, slgrk +(define_insn "*sub<mode>3_cconly" + [(set (reg CC_REGNUM) + (compare (minus:GPR (match_operand:GPR 1 "register_operand" "0,d,0,0") + (match_operand:GPR 2 "general_operand" "d,d,R,T")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=d,d,d,d"))] + "s390_match_ccmode (insn, CCLmode)" + "@ + sl<g>r\t%0,%2 + sl<g>rk\t%0,%1,%2 + sl<g>\t%0,%2 + sl<y>\t%0,%2" + [(set_attr "op_type" "RR<E>,RRF,RX<Y>,RXY") + (set_attr "cpu_facility" "*,z196,*,*") + (set_attr "z10prop" "z10_super_c_E1,*,z10_super_E1,z10_super_E1")]) + + +; slr, sl, sly, slgr, slg, slrk, slgrk +(define_insn "*sub<mode>3_cconly2" + [(set (reg CC_REGNUM) + (compare (match_operand:GPR 1 "register_operand" "0,d,0,0") + (match_operand:GPR 2 "general_operand" "d,d,R,T"))) + (clobber (match_scratch:GPR 0 "=d,d,d,d"))] + "s390_match_ccmode (insn, CCL3mode)" + "@ + sl<g>r\t%0,%2 + sl<g>rk\t%0,%1,%2 + sl<g>\t%0,%2 + sl<y>\t%0,%2" + [(set_attr "op_type" "RR<E>,RRF,RX<Y>,RXY") + (set_attr "cpu_facility" "*,z196,*,*") + (set_attr "z10prop" "z10_super_c_E1,*,z10_super_E1,z10_super_E1")]) + + +; +; sub(tf|df|sf|td|dd)3 instruction pattern(s). +; + +; sxbr, sdbr, sebr, sdb, seb, sxtr, sdtr +(define_insn "sub<mode>3" + [(set (match_operand:FP 0 "register_operand" "=f, f") + (minus:FP (match_operand:FP 1 "register_operand" "<f0>,0") + (match_operand:FP 2 "general_operand" "f,<Rf>"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_HARD_FLOAT" + "@ + s<xde><bt>r\t%0,<op1>%2 + s<xde>b\t%0,%2" + [(set_attr "op_type" "<RRer>,RXE") + (set_attr "type" "fsimp<mode>")]) + +; sxbr, sdbr, sebr, sdb, seb, sxtr, sdtr +(define_insn "*sub<mode>3_cc" + [(set (reg CC_REGNUM) + (compare (minus:FP (match_operand:FP 1 "nonimmediate_operand" "<f0>,0") + (match_operand:FP 2 "general_operand" "f,<Rf>")) + (match_operand:FP 3 "const0_operand" ""))) + (set (match_operand:FP 0 "register_operand" "=f,f") + (minus:FP (match_dup 1) (match_dup 2)))] + "s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT" + "@ + s<xde><bt>r\t%0,<op1>%2 + s<xde>b\t%0,%2" + [(set_attr "op_type" "<RRer>,RXE") + (set_attr "type" "fsimp<mode>")]) + +; sxbr, sdbr, sebr, sdb, seb, sxtr, sdtr +(define_insn "*sub<mode>3_cconly" + [(set (reg CC_REGNUM) + (compare (minus:FP (match_operand:FP 1 "nonimmediate_operand" "<f0>,0") + (match_operand:FP 2 "general_operand" "f,<Rf>")) + (match_operand:FP 3 "const0_operand" ""))) + (clobber (match_scratch:FP 0 "=f,f"))] + "s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT" + "@ + s<xde><bt>r\t%0,<op1>%2 + s<xde>b\t%0,%2" + [(set_attr "op_type" "<RRer>,RXE") + (set_attr "type" "fsimp<mode>")]) + + +;; +;;- Conditional add/subtract instructions. +;; + +; +; add(di|si)cc instruction pattern(s). +; + +; the following 4 patterns are used when the result of an add with +; carry is checked for an overflow condition + +; op1 + op2 + c < op1 + +; alcr, alc, alcgr, alcg +(define_insn "*add<mode>3_alc_carry1_cc" + [(set (reg CC_REGNUM) + (compare + (plus:GPR (plus:GPR (match_operand:GPR 3 "s390_alc_comparison" "") + (match_operand:GPR 1 "nonimmediate_operand" "%0,0")) + (match_operand:GPR 2 "general_operand" "d,RT")) + (match_dup 1))) + (set (match_operand:GPR 0 "register_operand" "=d,d") + (plus:GPR (plus:GPR (match_dup 3) (match_dup 1)) (match_dup 2)))] + "s390_match_ccmode (insn, CCL1mode) && TARGET_CPU_ZARCH" + "@ + alc<g>r\t%0,%2 + alc<g>\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z196prop" "z196_alone,z196_alone")]) + +; alcr, alc, alcgr, alcg +(define_insn "*add<mode>3_alc_carry1_cconly" + [(set (reg CC_REGNUM) + (compare + (plus:GPR (plus:GPR (match_operand:GPR 3 "s390_alc_comparison" "") + (match_operand:GPR 1 "nonimmediate_operand" "%0,0")) + (match_operand:GPR 2 "general_operand" "d,RT")) + (match_dup 1))) + (clobber (match_scratch:GPR 0 "=d,d"))] + "s390_match_ccmode (insn, CCL1mode) && TARGET_CPU_ZARCH" + "@ + alc<g>r\t%0,%2 + alc<g>\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z196prop" "z196_alone,z196_alone")]) + +; op1 + op2 + c < op2 + +; alcr, alc, alcgr, alcg +(define_insn "*add<mode>3_alc_carry2_cc" + [(set (reg CC_REGNUM) + (compare + (plus:GPR (plus:GPR (match_operand:GPR 3 "s390_alc_comparison" "") + (match_operand:GPR 1 "nonimmediate_operand" "%0,0")) + (match_operand:GPR 2 "general_operand" "d,RT")) + (match_dup 2))) + (set (match_operand:GPR 0 "register_operand" "=d,d") + (plus:GPR (plus:GPR (match_dup 3) (match_dup 1)) (match_dup 2)))] + "s390_match_ccmode (insn, CCL1mode) && TARGET_CPU_ZARCH" + "@ + alc<g>r\t%0,%2 + alc<g>\t%0,%2" + [(set_attr "op_type" "RRE,RXY")]) + +; alcr, alc, alcgr, alcg +(define_insn "*add<mode>3_alc_carry2_cconly" + [(set (reg CC_REGNUM) + (compare + (plus:GPR (plus:GPR (match_operand:GPR 3 "s390_alc_comparison" "") + (match_operand:GPR 1 "nonimmediate_operand" "%0,0")) + (match_operand:GPR 2 "general_operand" "d,RT")) + (match_dup 2))) + (clobber (match_scratch:GPR 0 "=d,d"))] + "s390_match_ccmode (insn, CCL1mode) && TARGET_CPU_ZARCH" + "@ + alc<g>r\t%0,%2 + alc<g>\t%0,%2" + [(set_attr "op_type" "RRE,RXY")]) + +; alcr, alc, alcgr, alcg +(define_insn "*add<mode>3_alc_cc" + [(set (reg CC_REGNUM) + (compare + (plus:GPR (plus:GPR (match_operand:GPR 3 "s390_alc_comparison" "") + (match_operand:GPR 1 "nonimmediate_operand" "%0,0")) + (match_operand:GPR 2 "general_operand" "d,RT")) + (const_int 0))) + (set (match_operand:GPR 0 "register_operand" "=d,d") + (plus:GPR (plus:GPR (match_dup 3) (match_dup 1)) (match_dup 2)))] + "s390_match_ccmode (insn, CCLmode) && TARGET_CPU_ZARCH" + "@ + alc<g>r\t%0,%2 + alc<g>\t%0,%2" + [(set_attr "op_type" "RRE,RXY")]) + +; alcr, alc, alcgr, alcg +(define_insn "*add<mode>3_alc" + [(set (match_operand:GPR 0 "register_operand" "=d,d") + (plus:GPR (plus:GPR (match_operand:GPR 3 "s390_alc_comparison" "") + (match_operand:GPR 1 "nonimmediate_operand" "%0,0")) + (match_operand:GPR 2 "general_operand" "d,RT"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_CPU_ZARCH" + "@ + alc<g>r\t%0,%2 + alc<g>\t%0,%2" + [(set_attr "op_type" "RRE,RXY")]) + +; slbr, slb, slbgr, slbg +(define_insn "*sub<mode>3_slb_cc" + [(set (reg CC_REGNUM) + (compare + (minus:GPR (minus:GPR (match_operand:GPR 1 "nonimmediate_operand" "0,0") + (match_operand:GPR 2 "general_operand" "d,RT")) + (match_operand:GPR 3 "s390_slb_comparison" "")) + (const_int 0))) + (set (match_operand:GPR 0 "register_operand" "=d,d") + (minus:GPR (minus:GPR (match_dup 1) (match_dup 2)) (match_dup 3)))] + "s390_match_ccmode (insn, CCLmode) && TARGET_CPU_ZARCH" + "@ + slb<g>r\t%0,%2 + slb<g>\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_c,*")]) + +; slbr, slb, slbgr, slbg +(define_insn "*sub<mode>3_slb" + [(set (match_operand:GPR 0 "register_operand" "=d,d") + (minus:GPR (minus:GPR (match_operand:GPR 1 "nonimmediate_operand" "0,0") + (match_operand:GPR 2 "general_operand" "d,RT")) + (match_operand:GPR 3 "s390_slb_comparison" ""))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_CPU_ZARCH" + "@ + slb<g>r\t%0,%2 + slb<g>\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_c,*")]) + +(define_expand "add<mode>cc" + [(match_operand:GPR 0 "register_operand" "") + (match_operand 1 "comparison_operator" "") + (match_operand:GPR 2 "register_operand" "") + (match_operand:GPR 3 "const_int_operand" "")] + "TARGET_CPU_ZARCH" + "if (!s390_expand_addcc (GET_CODE (operands[1]), + XEXP (operands[1], 0), XEXP (operands[1], 1), + operands[0], operands[2], + operands[3])) FAIL; DONE;") + +; +; scond instruction pattern(s). +; + +(define_insn_and_split "*scond<mode>" + [(set (match_operand:GPR 0 "register_operand" "=&d") + (match_operand:GPR 1 "s390_alc_comparison" "")) + (clobber (reg:CC CC_REGNUM))] + "TARGET_CPU_ZARCH" + "#" + "&& reload_completed" + [(set (match_dup 0) (const_int 0)) + (parallel + [(set (match_dup 0) (plus:GPR (plus:GPR (match_dup 1) (match_dup 0)) + (match_dup 0))) + (clobber (reg:CC CC_REGNUM))])] + "") + +(define_insn_and_split "*scond<mode>_neg" + [(set (match_operand:GPR 0 "register_operand" "=&d") + (match_operand:GPR 1 "s390_slb_comparison" "")) + (clobber (reg:CC CC_REGNUM))] + "TARGET_CPU_ZARCH" + "#" + "&& reload_completed" + [(set (match_dup 0) (const_int 0)) + (parallel + [(set (match_dup 0) (minus:GPR (minus:GPR (match_dup 0) (match_dup 0)) + (match_dup 1))) + (clobber (reg:CC CC_REGNUM))]) + (parallel + [(set (match_dup 0) (neg:GPR (match_dup 0))) + (clobber (reg:CC CC_REGNUM))])] + "") + + +(define_expand "cstore<mode>4" + [(set (match_operand:SI 0 "register_operand" "") + (match_operator:SI 1 "s390_scond_operator" + [(match_operand:GPR 2 "register_operand" "") + (match_operand:GPR 3 "general_operand" "")]))] + "TARGET_CPU_ZARCH" + "if (!s390_expand_addcc (GET_CODE (operands[1]), operands[2], operands[3], + operands[0], const0_rtx, const1_rtx)) FAIL; DONE;") + +(define_expand "cstorecc4" + [(parallel + [(set (match_operand:SI 0 "register_operand" "") + (match_operator:SI 1 "s390_eqne_operator" + [(match_operand:CCZ1 2 "register_operand") + (match_operand 3 "const0_operand")])) + (clobber (reg:CC CC_REGNUM))])] + "" + "emit_insn (gen_sne (operands[0], operands[2])); + if (GET_CODE (operands[1]) == EQ) + emit_insn (gen_xorsi3 (operands[0], operands[0], const1_rtx)); + DONE;") + +(define_insn_and_split "sne" + [(set (match_operand:SI 0 "register_operand" "=d") + (ne:SI (match_operand:CCZ1 1 "register_operand" "0") + (const_int 0))) + (clobber (reg:CC CC_REGNUM))] + "" + "#" + "reload_completed" + [(parallel + [(set (match_dup 0) (ashiftrt:SI (match_dup 0) (const_int 28))) + (clobber (reg:CC CC_REGNUM))])]) + + +;; +;; - Conditional move instructions (introduced with z196) +;; + +(define_expand "mov<mode>cc" + [(set (match_operand:GPR 0 "nonimmediate_operand" "") + (if_then_else:GPR (match_operand 1 "comparison_operator" "") + (match_operand:GPR 2 "nonimmediate_operand" "") + (match_operand:GPR 3 "nonimmediate_operand" "")))] + "TARGET_Z196" + "operands[1] = s390_emit_compare (GET_CODE (operands[1]), + XEXP (operands[1], 0), XEXP (operands[1], 1));") + +; locr, loc, stoc, locgr, locg, stocg +(define_insn_and_split "*mov<mode>cc" + [(set (match_operand:GPR 0 "nonimmediate_operand" "=d,d, d, d,QS,QS,&d") + (if_then_else:GPR + (match_operator 1 "s390_comparison" + [(match_operand 2 "cc_reg_operand" " c,c, c, c, c, c, c") + (const_int 0)]) + (match_operand:GPR 3 "nonimmediate_operand" " d,0,QS, 0, d, 0,QS") + (match_operand:GPR 4 "nonimmediate_operand" " 0,d, 0,QS, 0, d,QS")))] + "TARGET_Z196" + "@ + loc<g>r%C1\t%0,%3 + loc<g>r%D1\t%0,%4 + loc<g>%C1\t%0,%3 + loc<g>%D1\t%0,%4 + stoc<g>%C1\t%3,%0 + stoc<g>%D1\t%4,%0 + #" + "&& reload_completed + && MEM_P (operands[3]) && MEM_P (operands[4])" + [(set (match_dup 0) + (if_then_else:GPR + (match_op_dup 1 [(match_dup 2) (const_int 0)]) + (match_dup 3) + (match_dup 0))) + (set (match_dup 0) + (if_then_else:GPR + (match_op_dup 1 [(match_dup 2) (const_int 0)]) + (match_dup 0) + (match_dup 4)))] + "" + [(set_attr "op_type" "RRF,RRF,RSY,RSY,RSY,RSY,*")]) + +;; +;;- Multiply instructions. +;; + +; +; muldi3 instruction pattern(s). +; + +(define_insn "*muldi3_sign" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (mult:DI (sign_extend:DI (match_operand:SI 2 "general_operand" "d,RT")) + (match_operand:DI 1 "register_operand" "0,0")))] + "TARGET_ZARCH" + "@ + msgfr\t%0,%2 + msgf\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "type" "imuldi")]) + +(define_insn "muldi3" + [(set (match_operand:DI 0 "register_operand" "=d,d,d,d") + (mult:DI (match_operand:DI 1 "nonimmediate_operand" "%0,0,0,0") + (match_operand:DI 2 "general_operand" "d,K,RT,Os")))] + "TARGET_ZARCH" + "@ + msgr\t%0,%2 + mghi\t%0,%h2 + msg\t%0,%2 + msgfi\t%0,%2" + [(set_attr "op_type" "RRE,RI,RXY,RIL") + (set_attr "type" "imuldi") + (set_attr "cpu_facility" "*,*,*,z10")]) + +; +; mulsi3 instruction pattern(s). +; + +(define_insn "*mulsi3_sign" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (mult:SI (sign_extend:SI (match_operand:HI 2 "memory_operand" "R,T")) + (match_operand:SI 1 "register_operand" "0,0")))] + "" + "@ + mh\t%0,%2 + mhy\t%0,%2" + [(set_attr "op_type" "RX,RXY") + (set_attr "type" "imulhi") + (set_attr "cpu_facility" "*,z10")]) + +(define_insn "mulsi3" + [(set (match_operand:SI 0 "register_operand" "=d,d,d,d,d") + (mult:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0,0") + (match_operand:SI 2 "general_operand" "d,K,R,T,Os")))] + "" + "@ + msr\t%0,%2 + mhi\t%0,%h2 + ms\t%0,%2 + msy\t%0,%2 + msfi\t%0,%2" + [(set_attr "op_type" "RRE,RI,RX,RXY,RIL") + (set_attr "type" "imulsi,imulhi,imulsi,imulsi,imulsi") + (set_attr "cpu_facility" "*,*,*,*,z10")]) + +; +; mulsidi3 instruction pattern(s). +; + +(define_insn "mulsidi3" + [(set (match_operand:DI 0 "register_operand" "=d,d,d") + (mult:DI (sign_extend:DI + (match_operand:SI 1 "register_operand" "%0,0,0")) + (sign_extend:DI + (match_operand:SI 2 "nonimmediate_operand" "d,R,T"))))] + "!TARGET_ZARCH" + "@ + mr\t%0,%2 + m\t%0,%2 + mfy\t%0,%2" + [(set_attr "op_type" "RR,RX,RXY") + (set_attr "type" "imulsi") + (set_attr "cpu_facility" "*,*,z10")]) + +; +; umulsidi3 instruction pattern(s). +; + +(define_insn "umulsidi3" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (mult:DI (zero_extend:DI + (match_operand:SI 1 "register_operand" "%0,0")) + (zero_extend:DI + (match_operand:SI 2 "nonimmediate_operand" "d,RT"))))] + "!TARGET_ZARCH && TARGET_CPU_ZARCH" + "@ + mlr\t%0,%2 + ml\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "type" "imulsi")]) + +; +; mul(tf|df|sf|td|dd)3 instruction pattern(s). +; + +; mxbr, mdbr, meebr, mxb, mxb, meeb, mdtr, mxtr +(define_insn "mul<mode>3" + [(set (match_operand:FP 0 "register_operand" "=f,f") + (mult:FP (match_operand:FP 1 "nonimmediate_operand" "%<f0>,0") + (match_operand:FP 2 "general_operand" "f,<Rf>")))] + "TARGET_HARD_FLOAT" + "@ + m<xdee><bt>r\t%0,<op1>%2 + m<xdee>b\t%0,%2" + [(set_attr "op_type" "<RRer>,RXE") + (set_attr "type" "fmul<mode>")]) + +; madbr, maebr, maxb, madb, maeb +(define_insn "fma<mode>4" + [(set (match_operand:DSF 0 "register_operand" "=f,f") + (fma:DSF (match_operand:DSF 1 "nonimmediate_operand" "%f,f") + (match_operand:DSF 2 "nonimmediate_operand" "f,R") + (match_operand:DSF 3 "register_operand" "0,0")))] + "TARGET_HARD_FLOAT" + "@ + ma<xde>br\t%0,%1,%2 + ma<xde>b\t%0,%1,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "type" "fmadd<mode>")]) + +; msxbr, msdbr, msebr, msxb, msdb, mseb +(define_insn "fms<mode>4" + [(set (match_operand:DSF 0 "register_operand" "=f,f") + (fma:DSF (match_operand:DSF 1 "nonimmediate_operand" "%f,f") + (match_operand:DSF 2 "nonimmediate_operand" "f,R") + (neg:DSF (match_operand:DSF 3 "register_operand" "0,0"))))] + "TARGET_HARD_FLOAT" + "@ + ms<xde>br\t%0,%1,%2 + ms<xde>b\t%0,%1,%2" + [(set_attr "op_type" "RRE,RXE") + (set_attr "type" "fmadd<mode>")]) + +;; +;;- Divide and modulo instructions. +;; + +; +; divmoddi4 instruction pattern(s). +; + +(define_expand "divmoddi4" + [(parallel [(set (match_operand:DI 0 "general_operand" "") + (div:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "general_operand" ""))) + (set (match_operand:DI 3 "general_operand" "") + (mod:DI (match_dup 1) (match_dup 2)))]) + (clobber (match_dup 4))] + "TARGET_ZARCH" +{ + rtx insn, div_equal, mod_equal; + + div_equal = gen_rtx_DIV (DImode, operands[1], operands[2]); + mod_equal = gen_rtx_MOD (DImode, operands[1], operands[2]); + + operands[4] = gen_reg_rtx(TImode); + emit_insn (gen_divmodtidi3 (operands[4], operands[1], operands[2])); + + insn = emit_move_insn (operands[0], gen_lowpart (DImode, operands[4])); + set_unique_reg_note (insn, REG_EQUAL, div_equal); + + insn = emit_move_insn (operands[3], gen_highpart (DImode, operands[4])); + set_unique_reg_note (insn, REG_EQUAL, mod_equal); + + DONE; +}) + +(define_insn "divmodtidi3" + [(set (match_operand:TI 0 "register_operand" "=d,d") + (ior:TI + (ashift:TI + (zero_extend:TI + (mod:DI (match_operand:DI 1 "register_operand" "0,0") + (match_operand:DI 2 "general_operand" "d,RT"))) + (const_int 64)) + (zero_extend:TI (div:DI (match_dup 1) (match_dup 2)))))] + "TARGET_ZARCH" + "@ + dsgr\t%0,%2 + dsg\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "type" "idiv")]) + +(define_insn "divmodtisi3" + [(set (match_operand:TI 0 "register_operand" "=d,d") + (ior:TI + (ashift:TI + (zero_extend:TI + (mod:DI (match_operand:DI 1 "register_operand" "0,0") + (sign_extend:DI + (match_operand:SI 2 "nonimmediate_operand" "d,RT")))) + (const_int 64)) + (zero_extend:TI + (div:DI (match_dup 1) (sign_extend:DI (match_dup 2))))))] + "TARGET_ZARCH" + "@ + dsgfr\t%0,%2 + dsgf\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "type" "idiv")]) + +; +; udivmoddi4 instruction pattern(s). +; + +(define_expand "udivmoddi4" + [(parallel [(set (match_operand:DI 0 "general_operand" "") + (udiv:DI (match_operand:DI 1 "general_operand" "") + (match_operand:DI 2 "nonimmediate_operand" ""))) + (set (match_operand:DI 3 "general_operand" "") + (umod:DI (match_dup 1) (match_dup 2)))]) + (clobber (match_dup 4))] + "TARGET_ZARCH" +{ + rtx insn, div_equal, mod_equal, equal; + + div_equal = gen_rtx_UDIV (DImode, operands[1], operands[2]); + mod_equal = gen_rtx_UMOD (DImode, operands[1], operands[2]); + equal = gen_rtx_IOR (TImode, + gen_rtx_ASHIFT (TImode, + gen_rtx_ZERO_EXTEND (TImode, mod_equal), + GEN_INT (64)), + gen_rtx_ZERO_EXTEND (TImode, div_equal)); + + operands[4] = gen_reg_rtx(TImode); + emit_clobber (operands[4]); + emit_move_insn (gen_lowpart (DImode, operands[4]), operands[1]); + emit_move_insn (gen_highpart (DImode, operands[4]), const0_rtx); + + insn = emit_insn (gen_udivmodtidi3 (operands[4], operands[4], operands[2])); + set_unique_reg_note (insn, REG_EQUAL, equal); + + insn = emit_move_insn (operands[0], gen_lowpart (DImode, operands[4])); + set_unique_reg_note (insn, REG_EQUAL, div_equal); + + insn = emit_move_insn (operands[3], gen_highpart (DImode, operands[4])); + set_unique_reg_note (insn, REG_EQUAL, mod_equal); + + DONE; +}) + +(define_insn "udivmodtidi3" + [(set (match_operand:TI 0 "register_operand" "=d,d") + (ior:TI + (ashift:TI + (zero_extend:TI + (truncate:DI + (umod:TI (match_operand:TI 1 "register_operand" "0,0") + (zero_extend:TI + (match_operand:DI 2 "nonimmediate_operand" "d,RT"))))) + (const_int 64)) + (zero_extend:TI + (truncate:DI + (udiv:TI (match_dup 1) (zero_extend:TI (match_dup 2)))))))] + "TARGET_ZARCH" + "@ + dlgr\t%0,%2 + dlg\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "type" "idiv")]) + +; +; divmodsi4 instruction pattern(s). +; + +(define_expand "divmodsi4" + [(parallel [(set (match_operand:SI 0 "general_operand" "") + (div:SI (match_operand:SI 1 "general_operand" "") + (match_operand:SI 2 "nonimmediate_operand" ""))) + (set (match_operand:SI 3 "general_operand" "") + (mod:SI (match_dup 1) (match_dup 2)))]) + (clobber (match_dup 4))] + "!TARGET_ZARCH" +{ + rtx insn, div_equal, mod_equal, equal; + + div_equal = gen_rtx_DIV (SImode, operands[1], operands[2]); + mod_equal = gen_rtx_MOD (SImode, operands[1], operands[2]); + equal = gen_rtx_IOR (DImode, + gen_rtx_ASHIFT (DImode, + gen_rtx_ZERO_EXTEND (DImode, mod_equal), + GEN_INT (32)), + gen_rtx_ZERO_EXTEND (DImode, div_equal)); + + operands[4] = gen_reg_rtx(DImode); + emit_insn (gen_extendsidi2 (operands[4], operands[1])); + + insn = emit_insn (gen_divmoddisi3 (operands[4], operands[4], operands[2])); + set_unique_reg_note (insn, REG_EQUAL, equal); + + insn = emit_move_insn (operands[0], gen_lowpart (SImode, operands[4])); + set_unique_reg_note (insn, REG_EQUAL, div_equal); + + insn = emit_move_insn (operands[3], gen_highpart (SImode, operands[4])); + set_unique_reg_note (insn, REG_EQUAL, mod_equal); + + DONE; +}) + +(define_insn "divmoddisi3" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (ior:DI + (ashift:DI + (zero_extend:DI + (truncate:SI + (mod:DI (match_operand:DI 1 "register_operand" "0,0") + (sign_extend:DI + (match_operand:SI 2 "nonimmediate_operand" "d,R"))))) + (const_int 32)) + (zero_extend:DI + (truncate:SI + (div:DI (match_dup 1) (sign_extend:DI (match_dup 2)))))))] + "!TARGET_ZARCH" + "@ + dr\t%0,%2 + d\t%0,%2" + [(set_attr "op_type" "RR,RX") + (set_attr "type" "idiv")]) + +; +; udivsi3 and umodsi3 instruction pattern(s). +; + +(define_expand "udivmodsi4" + [(parallel [(set (match_operand:SI 0 "general_operand" "") + (udiv:SI (match_operand:SI 1 "general_operand" "") + (match_operand:SI 2 "nonimmediate_operand" ""))) + (set (match_operand:SI 3 "general_operand" "") + (umod:SI (match_dup 1) (match_dup 2)))]) + (clobber (match_dup 4))] + "!TARGET_ZARCH && TARGET_CPU_ZARCH" +{ + rtx insn, div_equal, mod_equal, equal; + + div_equal = gen_rtx_UDIV (SImode, operands[1], operands[2]); + mod_equal = gen_rtx_UMOD (SImode, operands[1], operands[2]); + equal = gen_rtx_IOR (DImode, + gen_rtx_ASHIFT (DImode, + gen_rtx_ZERO_EXTEND (DImode, mod_equal), + GEN_INT (32)), + gen_rtx_ZERO_EXTEND (DImode, div_equal)); + + operands[4] = gen_reg_rtx(DImode); + emit_clobber (operands[4]); + emit_move_insn (gen_lowpart (SImode, operands[4]), operands[1]); + emit_move_insn (gen_highpart (SImode, operands[4]), const0_rtx); + + insn = emit_insn (gen_udivmoddisi3 (operands[4], operands[4], operands[2])); + set_unique_reg_note (insn, REG_EQUAL, equal); + + insn = emit_move_insn (operands[0], gen_lowpart (SImode, operands[4])); + set_unique_reg_note (insn, REG_EQUAL, div_equal); + + insn = emit_move_insn (operands[3], gen_highpart (SImode, operands[4])); + set_unique_reg_note (insn, REG_EQUAL, mod_equal); + + DONE; +}) + +(define_insn "udivmoddisi3" + [(set (match_operand:DI 0 "register_operand" "=d,d") + (ior:DI + (ashift:DI + (zero_extend:DI + (truncate:SI + (umod:DI (match_operand:DI 1 "register_operand" "0,0") + (zero_extend:DI + (match_operand:SI 2 "nonimmediate_operand" "d,RT"))))) + (const_int 32)) + (zero_extend:DI + (truncate:SI + (udiv:DI (match_dup 1) (zero_extend:DI (match_dup 2)))))))] + "!TARGET_ZARCH && TARGET_CPU_ZARCH" + "@ + dlr\t%0,%2 + dl\t%0,%2" + [(set_attr "op_type" "RRE,RXY") + (set_attr "type" "idiv")]) + +(define_expand "udivsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (udiv:SI (match_operand:SI 1 "general_operand" "") + (match_operand:SI 2 "general_operand" ""))) + (clobber (match_dup 3))] + "!TARGET_ZARCH && !TARGET_CPU_ZARCH" +{ + rtx insn, udiv_equal, umod_equal, equal; + + udiv_equal = gen_rtx_UDIV (SImode, operands[1], operands[2]); + umod_equal = gen_rtx_UMOD (SImode, operands[1], operands[2]); + equal = gen_rtx_IOR (DImode, + gen_rtx_ASHIFT (DImode, + gen_rtx_ZERO_EXTEND (DImode, umod_equal), + GEN_INT (32)), + gen_rtx_ZERO_EXTEND (DImode, udiv_equal)); + + operands[3] = gen_reg_rtx (DImode); + + if (CONSTANT_P (operands[2])) + { + if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0) + { + rtx label1 = gen_label_rtx (); + + operands[1] = make_safe_from (operands[1], operands[0]); + emit_move_insn (operands[0], const0_rtx); + emit_cmp_and_jump_insns (operands[1], operands[2], LT, NULL_RTX, + SImode, 1, label1); + emit_move_insn (operands[0], const1_rtx); + emit_label (label1); + } + else + { + operands[2] = force_reg (SImode, operands[2]); + operands[2] = make_safe_from (operands[2], operands[0]); + + emit_insn (gen_zero_extendsidi2 (operands[3], operands[1])); + insn = emit_insn (gen_divmoddisi3 (operands[3], operands[3], + operands[2])); + set_unique_reg_note (insn, REG_EQUAL, equal); + + insn = emit_move_insn (operands[0], + gen_lowpart (SImode, operands[3])); + set_unique_reg_note (insn, REG_EQUAL, udiv_equal); + } + } + else + { + rtx label1 = gen_label_rtx (); + rtx label2 = gen_label_rtx (); + rtx label3 = gen_label_rtx (); + + operands[1] = force_reg (SImode, operands[1]); + operands[1] = make_safe_from (operands[1], operands[0]); + operands[2] = force_reg (SImode, operands[2]); + operands[2] = make_safe_from (operands[2], operands[0]); + + emit_move_insn (operands[0], const0_rtx); + emit_cmp_and_jump_insns (operands[2], operands[1], GT, NULL_RTX, + SImode, 1, label3); + emit_cmp_and_jump_insns (operands[2], const0_rtx, LT, NULL_RTX, + SImode, 0, label2); + emit_cmp_and_jump_insns (operands[2], const1_rtx, EQ, NULL_RTX, + SImode, 0, label1); + emit_insn (gen_zero_extendsidi2 (operands[3], operands[1])); + insn = emit_insn (gen_divmoddisi3 (operands[3], operands[3], + operands[2])); + set_unique_reg_note (insn, REG_EQUAL, equal); + + insn = emit_move_insn (operands[0], + gen_lowpart (SImode, operands[3])); + set_unique_reg_note (insn, REG_EQUAL, udiv_equal); + + emit_jump (label3); + emit_label (label1); + emit_move_insn (operands[0], operands[1]); + emit_jump (label3); + emit_label (label2); + emit_move_insn (operands[0], const1_rtx); + emit_label (label3); + } + emit_move_insn (operands[0], operands[0]); + DONE; +}) + +(define_expand "umodsi3" + [(set (match_operand:SI 0 "register_operand" "=d") + (umod:SI (match_operand:SI 1 "nonimmediate_operand" "") + (match_operand:SI 2 "nonimmediate_operand" ""))) + (clobber (match_dup 3))] + "!TARGET_ZARCH && !TARGET_CPU_ZARCH" +{ + rtx insn, udiv_equal, umod_equal, equal; + + udiv_equal = gen_rtx_UDIV (SImode, operands[1], operands[2]); + umod_equal = gen_rtx_UMOD (SImode, operands[1], operands[2]); + equal = gen_rtx_IOR (DImode, + gen_rtx_ASHIFT (DImode, + gen_rtx_ZERO_EXTEND (DImode, umod_equal), + GEN_INT (32)), + gen_rtx_ZERO_EXTEND (DImode, udiv_equal)); + + operands[3] = gen_reg_rtx (DImode); + + if (CONSTANT_P (operands[2])) + { + if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) <= 0) + { + rtx label1 = gen_label_rtx (); + + operands[1] = make_safe_from (operands[1], operands[0]); + emit_move_insn (operands[0], operands[1]); + emit_cmp_and_jump_insns (operands[0], operands[2], LT, NULL_RTX, + SImode, 1, label1); + emit_insn (gen_abssi2 (operands[0], operands[2])); + emit_insn (gen_addsi3 (operands[0], operands[0], operands[1])); + emit_label (label1); + } + else + { + operands[2] = force_reg (SImode, operands[2]); + operands[2] = make_safe_from (operands[2], operands[0]); + + emit_insn (gen_zero_extendsidi2 (operands[3], operands[1])); + insn = emit_insn (gen_divmoddisi3 (operands[3], operands[3], + operands[2])); + set_unique_reg_note (insn, REG_EQUAL, equal); + + insn = emit_move_insn (operands[0], + gen_highpart (SImode, operands[3])); + set_unique_reg_note (insn, REG_EQUAL, umod_equal); + } + } + else + { + rtx label1 = gen_label_rtx (); + rtx label2 = gen_label_rtx (); + rtx label3 = gen_label_rtx (); + + operands[1] = force_reg (SImode, operands[1]); + operands[1] = make_safe_from (operands[1], operands[0]); + operands[2] = force_reg (SImode, operands[2]); + operands[2] = make_safe_from (operands[2], operands[0]); + + emit_move_insn(operands[0], operands[1]); + emit_cmp_and_jump_insns (operands[2], operands[1], GT, NULL_RTX, + SImode, 1, label3); + emit_cmp_and_jump_insns (operands[2], const0_rtx, LT, NULL_RTX, + SImode, 0, label2); + emit_cmp_and_jump_insns (operands[2], const1_rtx, EQ, NULL_RTX, + SImode, 0, label1); + emit_insn (gen_zero_extendsidi2 (operands[3], operands[1])); + insn = emit_insn (gen_divmoddisi3 (operands[3], operands[3], + operands[2])); + set_unique_reg_note (insn, REG_EQUAL, equal); + + insn = emit_move_insn (operands[0], + gen_highpart (SImode, operands[3])); + set_unique_reg_note (insn, REG_EQUAL, umod_equal); + + emit_jump (label3); + emit_label (label1); + emit_move_insn (operands[0], const0_rtx); + emit_jump (label3); + emit_label (label2); + emit_insn (gen_subsi3 (operands[0], operands[0], operands[2])); + emit_label (label3); + } + DONE; +}) + +; +; div(df|sf)3 instruction pattern(s). +; + +; dxbr, ddbr, debr, dxb, ddb, deb, ddtr, dxtr +(define_insn "div<mode>3" + [(set (match_operand:FP 0 "register_operand" "=f,f") + (div:FP (match_operand:FP 1 "register_operand" "<f0>,0") + (match_operand:FP 2 "general_operand" "f,<Rf>")))] + "TARGET_HARD_FLOAT" + "@ + d<xde><bt>r\t%0,<op1>%2 + d<xde>b\t%0,%2" + [(set_attr "op_type" "<RRer>,RXE") + (set_attr "type" "fdiv<mode>")]) + + +;; +;;- And instructions. +;; + +(define_expand "and<mode>3" + [(set (match_operand:INT 0 "nonimmediate_operand" "") + (and:INT (match_operand:INT 1 "nonimmediate_operand" "") + (match_operand:INT 2 "general_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "" + "s390_expand_logical_operator (AND, <MODE>mode, operands); DONE;") + +; +; anddi3 instruction pattern(s). +; + +(define_insn "*anddi3_cc" + [(set (reg CC_REGNUM) + (compare (and:DI (match_operand:DI 1 "nonimmediate_operand" "%0,d, 0") + (match_operand:DI 2 "general_operand" " d,d,RT")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d,d, d") + (and:DI (match_dup 1) (match_dup 2)))] + "s390_match_ccmode(insn, CCTmode) && TARGET_ZARCH" + "@ + ngr\t%0,%2 + ngrk\t%0,%1,%2 + ng\t%0,%2" + [(set_attr "op_type" "RRE,RRF,RXY") + (set_attr "cpu_facility" "*,z196,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1")]) + +(define_insn "*anddi3_cconly" + [(set (reg CC_REGNUM) + (compare (and:DI (match_operand:DI 1 "nonimmediate_operand" "%0,d, 0") + (match_operand:DI 2 "general_operand" " d,d,RT")) + (const_int 0))) + (clobber (match_scratch:DI 0 "=d,d, d"))] + "s390_match_ccmode(insn, CCTmode) && TARGET_ZARCH + /* Do not steal TM patterns. */ + && s390_single_part (operands[2], DImode, HImode, 0) < 0" + "@ + ngr\t%0,%2 + ngrk\t%0,%1,%2 + ng\t%0,%2" + [(set_attr "op_type" "RRE,RRF,RXY") + (set_attr "cpu_facility" "*,z196,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1")]) + +(define_insn "*anddi3" + [(set (match_operand:DI 0 "nonimmediate_operand" + "=d,d, d, d, d, d, d, d,d,d, d, AQ,Q") + (and:DI (match_operand:DI 1 "nonimmediate_operand" + "%d,o, 0, 0, 0, 0, 0, 0,0,d, 0, 0,0") + (match_operand:DI 2 "general_operand" + "M, M,N0HDF,N1HDF,N2HDF,N3HDF,N0SDF,N1SDF,d,d,RT,NxQDF,Q"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + # + # + nihh\t%0,%j2 + nihl\t%0,%j2 + nilh\t%0,%j2 + nill\t%0,%j2 + nihf\t%0,%m2 + nilf\t%0,%m2 + ngr\t%0,%2 + ngrk\t%0,%1,%2 + ng\t%0,%2 + # + #" + [(set_attr "op_type" "RRE,RXE,RI,RI,RI,RI,RIL,RIL,RRE,RRF,RXY,SI,SS") + (set_attr "cpu_facility" "*,*,*,*,*,*,extimm,extimm,*,z196,*,*,*") + (set_attr "z10prop" "*, + *, + z10_super_E1, + z10_super_E1, + z10_super_E1, + z10_super_E1, + z10_super_E1, + z10_super_E1, + z10_super_E1, + *, + z10_super_E1, + *, + *")]) + +(define_split + [(set (match_operand:DI 0 "s_operand" "") + (and:DI (match_dup 0) (match_operand:DI 1 "immediate_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" + [(parallel + [(set (match_dup 0) (and:QI (match_dup 0) (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "s390_narrow_logical_operator (AND, &operands[0], &operands[1]);") + + +; +; andsi3 instruction pattern(s). +; + +(define_insn "*andsi3_cc" + [(set (reg CC_REGNUM) + (compare (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,d,0,0") + (match_operand:SI 2 "general_operand" "Os,d,d,R,T")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=d,d,d,d,d") + (and:SI (match_dup 1) (match_dup 2)))] + "s390_match_ccmode(insn, CCTmode)" + "@ + nilf\t%0,%o2 + nr\t%0,%2 + nrk\t%0,%1,%2 + n\t%0,%2 + ny\t%0,%2" + [(set_attr "op_type" "RIL,RR,RRF,RX,RXY") + (set_attr "cpu_facility" "*,*,z196,*,*") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,*,z10_super_E1,z10_super_E1")]) + +(define_insn "*andsi3_cconly" + [(set (reg CC_REGNUM) + (compare (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,d,0,0") + (match_operand:SI 2 "general_operand" "Os,d,d,R,T")) + (const_int 0))) + (clobber (match_scratch:SI 0 "=d,d,d,d,d"))] + "s390_match_ccmode(insn, CCTmode) + /* Do not steal TM patterns. */ + && s390_single_part (operands[2], SImode, HImode, 0) < 0" + "@ + nilf\t%0,%o2 + nr\t%0,%2 + nrk\t%0,%1,%2 + n\t%0,%2 + ny\t%0,%2" + [(set_attr "op_type" "RIL,RR,RRF,RX,RXY") + (set_attr "cpu_facility" "*,*,z196,*,*") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,*, + z10_super_E1,z10_super_E1")]) + +(define_insn "*andsi3_zarch" + [(set (match_operand:SI 0 "nonimmediate_operand" + "=d,d, d, d, d,d,d,d,d, AQ,Q") + (and:SI (match_operand:SI 1 "nonimmediate_operand" + "%d,o, 0, 0, 0,0,d,0,0, 0,0") + (match_operand:SI 2 "general_operand" + " M,M,N0HSF,N1HSF,Os,d,d,R,T,NxQSF,Q"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + # + # + nilh\t%0,%j2 + nill\t%0,%j2 + nilf\t%0,%o2 + nr\t%0,%2 + nrk\t%0,%1,%2 + n\t%0,%2 + ny\t%0,%2 + # + #" + [(set_attr "op_type" "RRE,RXE,RI,RI,RIL,RR,RRF,RX,RXY,SI,SS") + (set_attr "cpu_facility" "*,*,*,*,*,*,z196,*,*,*,*") + (set_attr "z10prop" "*, + *, + z10_super_E1, + z10_super_E1, + z10_super_E1, + z10_super_E1, + *, + z10_super_E1, + z10_super_E1, + *, + *")]) + +(define_insn "*andsi3_esa" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d, AQ,Q") + (and:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0, 0,0") + (match_operand:SI 2 "general_operand" " d,R,NxQSF,Q"))) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + nr\t%0,%2 + n\t%0,%2 + # + #" + [(set_attr "op_type" "RR,RX,SI,SS") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,*,*")]) + + +(define_split + [(set (match_operand:SI 0 "s_operand" "") + (and:SI (match_dup 0) (match_operand:SI 1 "immediate_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" + [(parallel + [(set (match_dup 0) (and:QI (match_dup 0) (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "s390_narrow_logical_operator (AND, &operands[0], &operands[1]);") + +; +; andhi3 instruction pattern(s). +; + +(define_insn "*andhi3_zarch" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d, AQ,Q") + (and:HI (match_operand:HI 1 "nonimmediate_operand" "%0,d,0, 0,0") + (match_operand:HI 2 "general_operand" " d,d,n,NxQHF,Q"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + nr\t%0,%2 + nrk\t%0,%1,%2 + nill\t%0,%x2 + # + #" + [(set_attr "op_type" "RR,RRF,RI,SI,SS") + (set_attr "cpu_facility" "*,z196,*,*,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1,*,*") +]) + +(define_insn "*andhi3_esa" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,AQ,Q") + (and:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0,0") + (match_operand:HI 2 "general_operand" "d,NxQHF,Q"))) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + nr\t%0,%2 + # + #" + [(set_attr "op_type" "RR,SI,SS") + (set_attr "z10prop" "z10_super_E1,*,*") +]) + +(define_split + [(set (match_operand:HI 0 "s_operand" "") + (and:HI (match_dup 0) (match_operand:HI 1 "immediate_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" + [(parallel + [(set (match_dup 0) (and:QI (match_dup 0) (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "s390_narrow_logical_operator (AND, &operands[0], &operands[1]);") + +; +; andqi3 instruction pattern(s). +; + +(define_insn "*andqi3_zarch" + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,Q,S,Q") + (and:QI (match_operand:QI 1 "nonimmediate_operand" "%0,d,0,0,0,0") + (match_operand:QI 2 "general_operand" " d,d,n,n,n,Q"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + nr\t%0,%2 + nrk\t%0,%1,%2 + nill\t%0,%b2 + ni\t%S0,%b2 + niy\t%S0,%b2 + #" + [(set_attr "op_type" "RR,RRF,RI,SI,SIY,SS") + (set_attr "cpu_facility" "*,z196,*,*,*,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1,z10_super,z10_super,*")]) + +(define_insn "*andqi3_esa" + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,Q,Q") + (and:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0") + (match_operand:QI 2 "general_operand" "d,n,Q"))) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + nr\t%0,%2 + ni\t%S0,%b2 + #" + [(set_attr "op_type" "RR,SI,SS") + (set_attr "z10prop" "z10_super_E1,z10_super,*")]) + +; +; Block and (NC) patterns. +; + +(define_insn "*nc" + [(set (match_operand:BLK 0 "memory_operand" "=Q") + (and:BLK (match_dup 0) + (match_operand:BLK 1 "memory_operand" "Q"))) + (use (match_operand 2 "const_int_operand" "n")) + (clobber (reg:CC CC_REGNUM))] + "INTVAL (operands[2]) >= 1 && INTVAL (operands[2]) <= 256" + "nc\t%O0(%2,%R0),%S1" + [(set_attr "op_type" "SS") + (set_attr "z196prop" "z196_cracked")]) + +(define_split + [(set (match_operand 0 "memory_operand" "") + (and (match_dup 0) + (match_operand 1 "memory_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed + && GET_MODE (operands[0]) == GET_MODE (operands[1]) + && GET_MODE_SIZE (GET_MODE (operands[0])) > 0" + [(parallel + [(set (match_dup 0) (and:BLK (match_dup 0) (match_dup 1))) + (use (match_dup 2)) + (clobber (reg:CC CC_REGNUM))])] +{ + operands[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (operands[0]))); + operands[0] = adjust_address (operands[0], BLKmode, 0); + operands[1] = adjust_address (operands[1], BLKmode, 0); +}) + +(define_peephole2 + [(parallel + [(set (match_operand:BLK 0 "memory_operand" "") + (and:BLK (match_dup 0) + (match_operand:BLK 1 "memory_operand" ""))) + (use (match_operand 2 "const_int_operand" "")) + (clobber (reg:CC CC_REGNUM))]) + (parallel + [(set (match_operand:BLK 3 "memory_operand" "") + (and:BLK (match_dup 3) + (match_operand:BLK 4 "memory_operand" ""))) + (use (match_operand 5 "const_int_operand" "")) + (clobber (reg:CC CC_REGNUM))])] + "s390_offset_p (operands[0], operands[3], operands[2]) + && s390_offset_p (operands[1], operands[4], operands[2]) + && !s390_overlap_p (operands[0], operands[1], + INTVAL (operands[2]) + INTVAL (operands[5])) + && INTVAL (operands[2]) + INTVAL (operands[5]) <= 256" + [(parallel + [(set (match_dup 6) (and:BLK (match_dup 6) (match_dup 7))) + (use (match_dup 8)) + (clobber (reg:CC CC_REGNUM))])] + "operands[6] = gen_rtx_MEM (BLKmode, XEXP (operands[0], 0)); + operands[7] = gen_rtx_MEM (BLKmode, XEXP (operands[1], 0)); + operands[8] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[5]));") + + +;; +;;- Bit set (inclusive or) instructions. +;; + +(define_expand "ior<mode>3" + [(set (match_operand:INT 0 "nonimmediate_operand" "") + (ior:INT (match_operand:INT 1 "nonimmediate_operand" "") + (match_operand:INT 2 "general_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "" + "s390_expand_logical_operator (IOR, <MODE>mode, operands); DONE;") + +; +; iordi3 instruction pattern(s). +; + +(define_insn "*iordi3_cc" + [(set (reg CC_REGNUM) + (compare (ior:DI (match_operand:DI 1 "nonimmediate_operand" "%0,d, 0") + (match_operand:DI 2 "general_operand" " d,d,RT")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d,d, d") + (ior:DI (match_dup 1) (match_dup 2)))] + "s390_match_ccmode(insn, CCTmode) && TARGET_ZARCH" + "@ + ogr\t%0,%2 + ogrk\t%0,%1,%2 + og\t%0,%2" + [(set_attr "op_type" "RRE,RRF,RXY") + (set_attr "cpu_facility" "*,z196,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1")]) + +(define_insn "*iordi3_cconly" + [(set (reg CC_REGNUM) + (compare (ior:DI (match_operand:DI 1 "nonimmediate_operand" "%0,d,0") + (match_operand:DI 2 "general_operand" " d,d,RT")) + (const_int 0))) + (clobber (match_scratch:DI 0 "=d,d,d"))] + "s390_match_ccmode(insn, CCTmode) && TARGET_ZARCH" + "@ + ogr\t%0,%2 + ogrk\t%0,%1,%2 + og\t%0,%2" + [(set_attr "op_type" "RRE,RRF,RXY") + (set_attr "cpu_facility" "*,z196,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1")]) + +(define_insn "*iordi3" + [(set (match_operand:DI 0 "nonimmediate_operand" + "=d, d, d, d, d, d,d,d, d, AQ,Q") + (ior:DI (match_operand:DI 1 "nonimmediate_operand" + " %0, 0, 0, 0, 0, 0,0,d, 0, 0,0") + (match_operand:DI 2 "general_operand" + "N0HD0,N1HD0,N2HD0,N3HD0,N0SD0,N1SD0,d,d,RT,NxQD0,Q"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + oihh\t%0,%i2 + oihl\t%0,%i2 + oilh\t%0,%i2 + oill\t%0,%i2 + oihf\t%0,%k2 + oilf\t%0,%k2 + ogr\t%0,%2 + ogrk\t%0,%1,%2 + og\t%0,%2 + # + #" + [(set_attr "op_type" "RI,RI,RI,RI,RIL,RIL,RRE,RRF,RXY,SI,SS") + (set_attr "cpu_facility" "*,*,*,*,extimm,extimm,*,z196,*,*,*") + (set_attr "z10prop" "z10_super_E1, + z10_super_E1, + z10_super_E1, + z10_super_E1, + z10_super_E1, + z10_super_E1, + z10_super_E1, + *, + z10_super_E1, + *, + *")]) + +(define_split + [(set (match_operand:DI 0 "s_operand" "") + (ior:DI (match_dup 0) (match_operand:DI 1 "immediate_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" + [(parallel + [(set (match_dup 0) (ior:QI (match_dup 0) (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "s390_narrow_logical_operator (IOR, &operands[0], &operands[1]);") + +; +; iorsi3 instruction pattern(s). +; + +(define_insn "*iorsi3_cc" + [(set (reg CC_REGNUM) + (compare (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,d,0,0") + (match_operand:SI 2 "general_operand" "Os,d,d,R,T")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=d,d,d,d,d") + (ior:SI (match_dup 1) (match_dup 2)))] + "s390_match_ccmode(insn, CCTmode)" + "@ + oilf\t%0,%o2 + or\t%0,%2 + ork\t%0,%1,%2 + o\t%0,%2 + oy\t%0,%2" + [(set_attr "op_type" "RIL,RR,RRF,RX,RXY") + (set_attr "cpu_facility" "*,*,z196,*,*") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,*,z10_super_E1,z10_super_E1")]) + +(define_insn "*iorsi3_cconly" + [(set (reg CC_REGNUM) + (compare (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,d,0,0") + (match_operand:SI 2 "general_operand" "Os,d,d,R,T")) + (const_int 0))) + (clobber (match_scratch:SI 0 "=d,d,d,d,d"))] + "s390_match_ccmode(insn, CCTmode)" + "@ + oilf\t%0,%o2 + or\t%0,%2 + ork\t%0,%1,%2 + o\t%0,%2 + oy\t%0,%2" + [(set_attr "op_type" "RIL,RR,RRF,RX,RXY") + (set_attr "cpu_facility" "*,*,z196,*,*") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,*,z10_super_E1,z10_super_E1")]) + +(define_insn "*iorsi3_zarch" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d, d, d,d,d,d,d, AQ,Q") + (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0, 0, 0,0,d,0,0, 0,0") + (match_operand:SI 2 "general_operand" "N0HS0,N1HS0,Os,d,d,R,T,NxQS0,Q"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + oilh\t%0,%i2 + oill\t%0,%i2 + oilf\t%0,%o2 + or\t%0,%2 + ork\t%0,%1,%2 + o\t%0,%2 + oy\t%0,%2 + # + #" + [(set_attr "op_type" "RI,RI,RIL,RR,RRF,RX,RXY,SI,SS") + (set_attr "cpu_facility" "*,*,*,*,z196,*,*,*,*") + (set_attr "z10prop" "z10_super_E1, + z10_super_E1, + z10_super_E1, + z10_super_E1, + *, + z10_super_E1, + z10_super_E1, + *, + *")]) + +(define_insn "*iorsi3_esa" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,AQ,Q") + (ior:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,0,0") + (match_operand:SI 2 "general_operand" "d,R,NxQS0,Q"))) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + or\t%0,%2 + o\t%0,%2 + # + #" + [(set_attr "op_type" "RR,RX,SI,SS") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,*,*")]) + +(define_split + [(set (match_operand:SI 0 "s_operand" "") + (ior:SI (match_dup 0) (match_operand:SI 1 "immediate_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" + [(parallel + [(set (match_dup 0) (ior:QI (match_dup 0) (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "s390_narrow_logical_operator (IOR, &operands[0], &operands[1]);") + +; +; iorhi3 instruction pattern(s). +; + +(define_insn "*iorhi3_zarch" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d, AQ,Q") + (ior:HI (match_operand:HI 1 "nonimmediate_operand" "%0,d,0, 0,0") + (match_operand:HI 2 "general_operand" " d,d,n,NxQH0,Q"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + or\t%0,%2 + ork\t%0,%1,%2 + oill\t%0,%x2 + # + #" + [(set_attr "op_type" "RR,RRF,RI,SI,SS") + (set_attr "cpu_facility" "*,z196,*,*,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1,*,*")]) + +(define_insn "*iorhi3_esa" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,AQ,Q") + (ior:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0,0") + (match_operand:HI 2 "general_operand" "d,NxQH0,Q"))) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + or\t%0,%2 + # + #" + [(set_attr "op_type" "RR,SI,SS") + (set_attr "z10prop" "z10_super_E1,*,*")]) + +(define_split + [(set (match_operand:HI 0 "s_operand" "") + (ior:HI (match_dup 0) (match_operand:HI 1 "immediate_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" + [(parallel + [(set (match_dup 0) (ior:QI (match_dup 0) (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "s390_narrow_logical_operator (IOR, &operands[0], &operands[1]);") + +; +; iorqi3 instruction pattern(s). +; + +(define_insn "*iorqi3_zarch" + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,Q,S,Q") + (ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,d,0,0,0,0") + (match_operand:QI 2 "general_operand" " d,d,n,n,n,Q"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + or\t%0,%2 + ork\t%0,%1,%2 + oill\t%0,%b2 + oi\t%S0,%b2 + oiy\t%S0,%b2 + #" + [(set_attr "op_type" "RR,RRF,RI,SI,SIY,SS") + (set_attr "cpu_facility" "*,z196,*,*,*,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1, + z10_super,z10_super,*")]) + +(define_insn "*iorqi3_esa" + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,Q,Q") + (ior:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,0") + (match_operand:QI 2 "general_operand" "d,n,Q"))) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + or\t%0,%2 + oi\t%S0,%b2 + #" + [(set_attr "op_type" "RR,SI,SS") + (set_attr "z10prop" "z10_super_E1,z10_super,*")]) + +; +; Block inclusive or (OC) patterns. +; + +(define_insn "*oc" + [(set (match_operand:BLK 0 "memory_operand" "=Q") + (ior:BLK (match_dup 0) + (match_operand:BLK 1 "memory_operand" "Q"))) + (use (match_operand 2 "const_int_operand" "n")) + (clobber (reg:CC CC_REGNUM))] + "INTVAL (operands[2]) >= 1 && INTVAL (operands[2]) <= 256" + "oc\t%O0(%2,%R0),%S1" + [(set_attr "op_type" "SS") + (set_attr "z196prop" "z196_cracked")]) + +(define_split + [(set (match_operand 0 "memory_operand" "") + (ior (match_dup 0) + (match_operand 1 "memory_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed + && GET_MODE (operands[0]) == GET_MODE (operands[1]) + && GET_MODE_SIZE (GET_MODE (operands[0])) > 0" + [(parallel + [(set (match_dup 0) (ior:BLK (match_dup 0) (match_dup 1))) + (use (match_dup 2)) + (clobber (reg:CC CC_REGNUM))])] +{ + operands[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (operands[0]))); + operands[0] = adjust_address (operands[0], BLKmode, 0); + operands[1] = adjust_address (operands[1], BLKmode, 0); +}) + +(define_peephole2 + [(parallel + [(set (match_operand:BLK 0 "memory_operand" "") + (ior:BLK (match_dup 0) + (match_operand:BLK 1 "memory_operand" ""))) + (use (match_operand 2 "const_int_operand" "")) + (clobber (reg:CC CC_REGNUM))]) + (parallel + [(set (match_operand:BLK 3 "memory_operand" "") + (ior:BLK (match_dup 3) + (match_operand:BLK 4 "memory_operand" ""))) + (use (match_operand 5 "const_int_operand" "")) + (clobber (reg:CC CC_REGNUM))])] + "s390_offset_p (operands[0], operands[3], operands[2]) + && s390_offset_p (operands[1], operands[4], operands[2]) + && !s390_overlap_p (operands[0], operands[1], + INTVAL (operands[2]) + INTVAL (operands[5])) + && INTVAL (operands[2]) + INTVAL (operands[5]) <= 256" + [(parallel + [(set (match_dup 6) (ior:BLK (match_dup 6) (match_dup 7))) + (use (match_dup 8)) + (clobber (reg:CC CC_REGNUM))])] + "operands[6] = gen_rtx_MEM (BLKmode, XEXP (operands[0], 0)); + operands[7] = gen_rtx_MEM (BLKmode, XEXP (operands[1], 0)); + operands[8] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[5]));") + + +;; +;;- Xor instructions. +;; + +(define_expand "xor<mode>3" + [(set (match_operand:INT 0 "nonimmediate_operand" "") + (xor:INT (match_operand:INT 1 "nonimmediate_operand" "") + (match_operand:INT 2 "general_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "" + "s390_expand_logical_operator (XOR, <MODE>mode, operands); DONE;") + +; +; xordi3 instruction pattern(s). +; + +(define_insn "*xordi3_cc" + [(set (reg CC_REGNUM) + (compare (xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0,d, 0") + (match_operand:DI 2 "general_operand" " d,d,RT")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d,d, d") + (xor:DI (match_dup 1) (match_dup 2)))] + "s390_match_ccmode(insn, CCTmode) && TARGET_ZARCH" + "@ + xgr\t%0,%2 + xgrk\t%0,%1,%2 + xg\t%0,%2" + [(set_attr "op_type" "RRE,RRF,RXY") + (set_attr "cpu_facility" "*,z196,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1")]) + +(define_insn "*xordi3_cconly" + [(set (reg CC_REGNUM) + (compare (xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0,d, 0") + (match_operand:DI 2 "general_operand" " d,d,RT")) + (const_int 0))) + (clobber (match_scratch:DI 0 "=d,d, d"))] + "s390_match_ccmode(insn, CCTmode) && TARGET_ZARCH" + "@ + xgr\t%0,%2 + xgrk\t%0,%1,%2 + xg\t%0,%2" + [(set_attr "op_type" "RRE,RRF,RXY") + (set_attr "cpu_facility" "*,z196,*") + (set_attr "z10prop" "z10_super_E1,*,z10_super_E1")]) + +(define_insn "*xordi3" + [(set (match_operand:DI 0 "nonimmediate_operand" "=d, d,d,d, d, AQ,Q") + (xor:DI (match_operand:DI 1 "nonimmediate_operand" "%0, 0,0,d, 0, 0,0") + (match_operand:DI 2 "general_operand" "N0SD0,N1SD0,d,d,RT,NxQD0,Q"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH && s390_logical_operator_ok_p (operands)" + "@ + xihf\t%0,%k2 + xilf\t%0,%k2 + xgr\t%0,%2 + xgrk\t%0,%1,%2 + xg\t%0,%2 + # + #" + [(set_attr "op_type" "RIL,RIL,RRE,RRF,RXY,SI,SS") + (set_attr "cpu_facility" "extimm,extimm,*,z196,*,*,*") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,z10_super_E1, + *,z10_super_E1,*,*")]) + +(define_split + [(set (match_operand:DI 0 "s_operand" "") + (xor:DI (match_dup 0) (match_operand:DI 1 "immediate_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" + [(parallel + [(set (match_dup 0) (xor:QI (match_dup 0) (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "s390_narrow_logical_operator (XOR, &operands[0], &operands[1]);") + +; +; xorsi3 instruction pattern(s). +; + +(define_insn "*xorsi3_cc" + [(set (reg CC_REGNUM) + (compare (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,d,0,0") + (match_operand:SI 2 "general_operand" "Os,d,d,R,T")) + (const_int 0))) + (set (match_operand:SI 0 "register_operand" "=d,d,d,d,d") + (xor:SI (match_dup 1) (match_dup 2)))] + "s390_match_ccmode(insn, CCTmode)" + "@ + xilf\t%0,%o2 + xr\t%0,%2 + xrk\t%0,%1,%2 + x\t%0,%2 + xy\t%0,%2" + [(set_attr "op_type" "RIL,RR,RRF,RX,RXY") + (set_attr "cpu_facility" "*,*,z196,*,*") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,*, + z10_super_E1,z10_super_E1")]) + +(define_insn "*xorsi3_cconly" + [(set (reg CC_REGNUM) + (compare (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,d,0,0") + (match_operand:SI 2 "general_operand" "Os,d,d,R,T")) + (const_int 0))) + (clobber (match_scratch:SI 0 "=d,d,d,d,d"))] + "s390_match_ccmode(insn, CCTmode)" + "@ + xilf\t%0,%o2 + xr\t%0,%2 + xrk\t%0,%1,%2 + x\t%0,%2 + xy\t%0,%2" + [(set_attr "op_type" "RIL,RR,RRF,RX,RXY") + (set_attr "cpu_facility" "*,*,z196,*,*") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,*, + z10_super_E1,z10_super_E1")]) + +(define_insn "*xorsi3" + [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d, AQ,Q") + (xor:SI (match_operand:SI 1 "nonimmediate_operand" "%0,0,d,0,0, 0,0") + (match_operand:SI 2 "general_operand" "Os,d,d,R,T,NxQS0,Q"))) + (clobber (reg:CC CC_REGNUM))] + "s390_logical_operator_ok_p (operands)" + "@ + xilf\t%0,%o2 + xr\t%0,%2 + xrk\t%0,%1,%2 + x\t%0,%2 + xy\t%0,%2 + # + #" + [(set_attr "op_type" "RIL,RR,RRF,RX,RXY,SI,SS") + (set_attr "cpu_facility" "*,*,z196,*,*,*,*") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,*, + z10_super_E1,z10_super_E1,*,*")]) + +(define_split + [(set (match_operand:SI 0 "s_operand" "") + (xor:SI (match_dup 0) (match_operand:SI 1 "immediate_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" + [(parallel + [(set (match_dup 0) (xor:QI (match_dup 0) (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "s390_narrow_logical_operator (XOR, &operands[0], &operands[1]);") + +; +; xorhi3 instruction pattern(s). +; + +(define_insn "*xorhi3" + [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d, AQ,Q") + (xor:HI (match_operand:HI 1 "nonimmediate_operand" "%0,0,d, 0,0") + (match_operand:HI 2 "general_operand" "Os,d,d,NxQH0,Q"))) + (clobber (reg:CC CC_REGNUM))] + "s390_logical_operator_ok_p (operands)" + "@ + xilf\t%0,%x2 + xr\t%0,%2 + xrk\t%0,%1,%2 + # + #" + [(set_attr "op_type" "RIL,RR,RRF,SI,SS") + (set_attr "cpu_facility" "*,*,z196,*,*") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,*,*,*")]) + +(define_split + [(set (match_operand:HI 0 "s_operand" "") + (xor:HI (match_dup 0) (match_operand:HI 1 "immediate_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed" + [(parallel + [(set (match_dup 0) (xor:QI (match_dup 0) (match_dup 1))) + (clobber (reg:CC CC_REGNUM))])] + "s390_narrow_logical_operator (XOR, &operands[0], &operands[1]);") + +; +; xorqi3 instruction pattern(s). +; + +(define_insn "*xorqi3" + [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,Q,S,Q") + (xor:QI (match_operand:QI 1 "nonimmediate_operand" "%0,0,d,0,0,0") + (match_operand:QI 2 "general_operand" "Os,d,d,n,n,Q"))) + (clobber (reg:CC CC_REGNUM))] + "s390_logical_operator_ok_p (operands)" + "@ + xilf\t%0,%b2 + xr\t%0,%2 + xrk\t%0,%1,%2 + xi\t%S0,%b2 + xiy\t%S0,%b2 + #" + [(set_attr "op_type" "RIL,RR,RRF,SI,SIY,SS") + (set_attr "cpu_facility" "*,*,z196,*,*,*") + (set_attr "z10prop" "z10_super_E1,z10_super_E1,*,z10_super,z10_super,*")]) + + +; +; Block exclusive or (XC) patterns. +; + +(define_insn "*xc" + [(set (match_operand:BLK 0 "memory_operand" "=Q") + (xor:BLK (match_dup 0) + (match_operand:BLK 1 "memory_operand" "Q"))) + (use (match_operand 2 "const_int_operand" "n")) + (clobber (reg:CC CC_REGNUM))] + "INTVAL (operands[2]) >= 1 && INTVAL (operands[2]) <= 256" + "xc\t%O0(%2,%R0),%S1" + [(set_attr "op_type" "SS")]) + +(define_split + [(set (match_operand 0 "memory_operand" "") + (xor (match_dup 0) + (match_operand 1 "memory_operand" ""))) + (clobber (reg:CC CC_REGNUM))] + "reload_completed + && GET_MODE (operands[0]) == GET_MODE (operands[1]) + && GET_MODE_SIZE (GET_MODE (operands[0])) > 0" + [(parallel + [(set (match_dup 0) (xor:BLK (match_dup 0) (match_dup 1))) + (use (match_dup 2)) + (clobber (reg:CC CC_REGNUM))])] +{ + operands[2] = GEN_INT (GET_MODE_SIZE (GET_MODE (operands[0]))); + operands[0] = adjust_address (operands[0], BLKmode, 0); + operands[1] = adjust_address (operands[1], BLKmode, 0); +}) + +(define_peephole2 + [(parallel + [(set (match_operand:BLK 0 "memory_operand" "") + (xor:BLK (match_dup 0) + (match_operand:BLK 1 "memory_operand" ""))) + (use (match_operand 2 "const_int_operand" "")) + (clobber (reg:CC CC_REGNUM))]) + (parallel + [(set (match_operand:BLK 3 "memory_operand" "") + (xor:BLK (match_dup 3) + (match_operand:BLK 4 "memory_operand" ""))) + (use (match_operand 5 "const_int_operand" "")) + (clobber (reg:CC CC_REGNUM))])] + "s390_offset_p (operands[0], operands[3], operands[2]) + && s390_offset_p (operands[1], operands[4], operands[2]) + && !s390_overlap_p (operands[0], operands[1], + INTVAL (operands[2]) + INTVAL (operands[5])) + && INTVAL (operands[2]) + INTVAL (operands[5]) <= 256" + [(parallel + [(set (match_dup 6) (xor:BLK (match_dup 6) (match_dup 7))) + (use (match_dup 8)) + (clobber (reg:CC CC_REGNUM))])] + "operands[6] = gen_rtx_MEM (BLKmode, XEXP (operands[0], 0)); + operands[7] = gen_rtx_MEM (BLKmode, XEXP (operands[1], 0)); + operands[8] = GEN_INT (INTVAL (operands[2]) + INTVAL (operands[5]));") + +; +; Block xor (XC) patterns with src == dest. +; + +(define_insn "*xc_zero" + [(set (match_operand:BLK 0 "memory_operand" "=Q") + (const_int 0)) + (use (match_operand 1 "const_int_operand" "n")) + (clobber (reg:CC CC_REGNUM))] + "INTVAL (operands[1]) >= 1 && INTVAL (operands[1]) <= 256" + "xc\t%O0(%1,%R0),%S0" + [(set_attr "op_type" "SS") + (set_attr "z196prop" "z196_cracked")]) + +(define_peephole2 + [(parallel + [(set (match_operand:BLK 0 "memory_operand" "") + (const_int 0)) + (use (match_operand 1 "const_int_operand" "")) + (clobber (reg:CC CC_REGNUM))]) + (parallel + [(set (match_operand:BLK 2 "memory_operand" "") + (const_int 0)) + (use (match_operand 3 "const_int_operand" "")) + (clobber (reg:CC CC_REGNUM))])] + "s390_offset_p (operands[0], operands[2], operands[1]) + && INTVAL (operands[1]) + INTVAL (operands[3]) <= 256" + [(parallel + [(set (match_dup 4) (const_int 0)) + (use (match_dup 5)) + (clobber (reg:CC CC_REGNUM))])] + "operands[4] = gen_rtx_MEM (BLKmode, XEXP (operands[0], 0)); + operands[5] = GEN_INT (INTVAL (operands[1]) + INTVAL (operands[3]));") + + +;; +;;- Negate instructions. +;; + +; +; neg(di|si)2 instruction pattern(s). +; + +(define_expand "neg<mode>2" + [(parallel + [(set (match_operand:DSI 0 "register_operand" "=d") + (neg:DSI (match_operand:DSI 1 "register_operand" "d"))) + (clobber (reg:CC CC_REGNUM))])] + "" + "") + +(define_insn "*negdi2_sign_cc" + [(set (reg CC_REGNUM) + (compare (neg:DI (ashiftrt:DI (ashift:DI (subreg:DI + (match_operand:SI 1 "register_operand" "d") 0) + (const_int 32)) (const_int 32))) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d") + (neg:DI (sign_extend:DI (match_dup 1))))] + "TARGET_ZARCH && s390_match_ccmode (insn, CCAmode)" + "lcgfr\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "z10prop" "z10_c")]) + +(define_insn "*negdi2_sign" + [(set (match_operand:DI 0 "register_operand" "=d") + (neg:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" + "lcgfr\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "z10prop" "z10_c")]) + +; lcr, lcgr +(define_insn "*neg<mode>2_cc" + [(set (reg CC_REGNUM) + (compare (neg:GPR (match_operand:GPR 1 "register_operand" "d")) + (const_int 0))) + (set (match_operand:GPR 0 "register_operand" "=d") + (neg:GPR (match_dup 1)))] + "s390_match_ccmode (insn, CCAmode)" + "lc<g>r\t%0,%1" + [(set_attr "op_type" "RR<E>") + (set_attr "z10prop" "z10_super_c_E1")]) + +; lcr, lcgr +(define_insn "*neg<mode>2_cconly" + [(set (reg CC_REGNUM) + (compare (neg:GPR (match_operand:GPR 1 "register_operand" "d")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=d"))] + "s390_match_ccmode (insn, CCAmode)" + "lc<g>r\t%0,%1" + [(set_attr "op_type" "RR<E>") + (set_attr "z10prop" "z10_super_c_E1")]) + +; lcr, lcgr +(define_insn "*neg<mode>2" + [(set (match_operand:GPR 0 "register_operand" "=d") + (neg:GPR (match_operand:GPR 1 "register_operand" "d"))) + (clobber (reg:CC CC_REGNUM))] + "" + "lc<g>r\t%0,%1" + [(set_attr "op_type" "RR<E>") + (set_attr "z10prop" "z10_super_c_E1")]) + +(define_insn_and_split "*negdi2_31" + [(set (match_operand:DI 0 "register_operand" "=d") + (neg:DI (match_operand:DI 1 "register_operand" "d"))) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH" + "#" + "&& reload_completed" + [(parallel + [(set (match_dup 2) (neg:SI (match_dup 3))) + (clobber (reg:CC CC_REGNUM))]) + (parallel + [(set (reg:CCAP CC_REGNUM) + (compare:CCAP (neg:SI (match_dup 5)) (const_int 0))) + (set (match_dup 4) (neg:SI (match_dup 5)))]) + (set (pc) + (if_then_else (ne (reg:CCAP CC_REGNUM) (const_int 0)) + (pc) + (label_ref (match_dup 6)))) + (parallel + [(set (match_dup 2) (plus:SI (match_dup 2) (const_int -1))) + (clobber (reg:CC CC_REGNUM))]) + (match_dup 6)] + "operands[2] = operand_subword (operands[0], 0, 0, DImode); + operands[3] = operand_subword (operands[1], 0, 0, DImode); + operands[4] = operand_subword (operands[0], 1, 0, DImode); + operands[5] = operand_subword (operands[1], 1, 0, DImode); + operands[6] = gen_label_rtx ();") + +; +; neg(df|sf)2 instruction pattern(s). +; + +(define_expand "neg<mode>2" + [(parallel + [(set (match_operand:BFP 0 "register_operand" "=f") + (neg:BFP (match_operand:BFP 1 "register_operand" "f"))) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_HARD_FLOAT" + "") + +; lcxbr, lcdbr, lcebr +(define_insn "*neg<mode>2_cc" + [(set (reg CC_REGNUM) + (compare (neg:BFP (match_operand:BFP 1 "register_operand" "f")) + (match_operand:BFP 2 "const0_operand" ""))) + (set (match_operand:BFP 0 "register_operand" "=f") + (neg:BFP (match_dup 1)))] + "s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT" + "lc<xde>br\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + +; lcxbr, lcdbr, lcebr +(define_insn "*neg<mode>2_cconly" + [(set (reg CC_REGNUM) + (compare (neg:BFP (match_operand:BFP 1 "register_operand" "f")) + (match_operand:BFP 2 "const0_operand" ""))) + (clobber (match_scratch:BFP 0 "=f"))] + "s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT" + "lc<xde>br\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + +; lcdfr +(define_insn "*neg<mode>2_nocc" + [(set (match_operand:FP 0 "register_operand" "=f") + (neg:FP (match_operand:FP 1 "register_operand" "<fT0>")))] + "TARGET_DFP" + "lcdfr\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + +; lcxbr, lcdbr, lcebr +(define_insn "*neg<mode>2" + [(set (match_operand:BFP 0 "register_operand" "=f") + (neg:BFP (match_operand:BFP 1 "register_operand" "f"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_HARD_FLOAT" + "lc<xde>br\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + + +;; +;;- Absolute value instructions. +;; + +; +; abs(di|si)2 instruction pattern(s). +; + +(define_insn "*absdi2_sign_cc" + [(set (reg CC_REGNUM) + (compare (abs:DI (ashiftrt:DI (ashift:DI (subreg:DI + (match_operand:SI 1 "register_operand" "d") 0) + (const_int 32)) (const_int 32))) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d") + (abs:DI (sign_extend:DI (match_dup 1))))] + "TARGET_ZARCH && s390_match_ccmode (insn, CCAmode)" + "lpgfr\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "z10prop" "z10_c")]) + +(define_insn "*absdi2_sign" + [(set (match_operand:DI 0 "register_operand" "=d") + (abs:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" + "lpgfr\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "z10prop" "z10_c")]) + +; lpr, lpgr +(define_insn "*abs<mode>2_cc" + [(set (reg CC_REGNUM) + (compare (abs:GPR (match_operand:DI 1 "register_operand" "d")) + (const_int 0))) + (set (match_operand:GPR 0 "register_operand" "=d") + (abs:GPR (match_dup 1)))] + "s390_match_ccmode (insn, CCAmode)" + "lp<g>r\t%0,%1" + [(set_attr "op_type" "RR<E>") + (set_attr "z10prop" "z10_c")]) + +; lpr, lpgr +(define_insn "*abs<mode>2_cconly" + [(set (reg CC_REGNUM) + (compare (abs:GPR (match_operand:GPR 1 "register_operand" "d")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=d"))] + "s390_match_ccmode (insn, CCAmode)" + "lp<g>r\t%0,%1" + [(set_attr "op_type" "RR<E>") + (set_attr "z10prop" "z10_c")]) + +; lpr, lpgr +(define_insn "abs<mode>2" + [(set (match_operand:GPR 0 "register_operand" "=d") + (abs:GPR (match_operand:GPR 1 "register_operand" "d"))) + (clobber (reg:CC CC_REGNUM))] + "" + "lp<g>r\t%0,%1" + [(set_attr "op_type" "RR<E>") + (set_attr "z10prop" "z10_c")]) + +; +; abs(df|sf)2 instruction pattern(s). +; + +(define_expand "abs<mode>2" + [(parallel + [(set (match_operand:BFP 0 "register_operand" "=f") + (abs:BFP (match_operand:BFP 1 "register_operand" "f"))) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_HARD_FLOAT" + "") + +; lpxbr, lpdbr, lpebr +(define_insn "*abs<mode>2_cc" + [(set (reg CC_REGNUM) + (compare (abs:BFP (match_operand:BFP 1 "register_operand" "f")) + (match_operand:BFP 2 "const0_operand" ""))) + (set (match_operand:BFP 0 "register_operand" "=f") + (abs:BFP (match_dup 1)))] + "s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT" + "lp<xde>br\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + +; lpxbr, lpdbr, lpebr +(define_insn "*abs<mode>2_cconly" + [(set (reg CC_REGNUM) + (compare (abs:BFP (match_operand:BFP 1 "register_operand" "f")) + (match_operand:BFP 2 "const0_operand" ""))) + (clobber (match_scratch:BFP 0 "=f"))] + "s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT" + "lp<xde>br\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + +; lpdfr +(define_insn "*abs<mode>2_nocc" + [(set (match_operand:FP 0 "register_operand" "=f") + (abs:FP (match_operand:FP 1 "register_operand" "<fT0>")))] + "TARGET_DFP" + "lpdfr\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + +; lpxbr, lpdbr, lpebr +(define_insn "*abs<mode>2" + [(set (match_operand:BFP 0 "register_operand" "=f") + (abs:BFP (match_operand:BFP 1 "register_operand" "f"))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_HARD_FLOAT" + "lp<xde>br\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + + +;; +;;- Negated absolute value instructions +;; + +; +; Integer +; + +(define_insn "*negabsdi2_sign_cc" + [(set (reg CC_REGNUM) + (compare (neg:DI (abs:DI (ashiftrt:DI (ashift:DI (subreg:DI + (match_operand:SI 1 "register_operand" "d") 0) + (const_int 32)) (const_int 32)))) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d") + (neg:DI (abs:DI (sign_extend:DI (match_dup 1)))))] + "TARGET_ZARCH && s390_match_ccmode (insn, CCAmode)" + "lngfr\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "z10prop" "z10_c")]) + +(define_insn "*negabsdi2_sign" + [(set (match_operand:DI 0 "register_operand" "=d") + (neg:DI (abs:DI (sign_extend:DI + (match_operand:SI 1 "register_operand" "d"))))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" + "lngfr\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "z10prop" "z10_c")]) + +; lnr, lngr +(define_insn "*negabs<mode>2_cc" + [(set (reg CC_REGNUM) + (compare (neg:GPR (abs:GPR (match_operand:GPR 1 "register_operand" "d"))) + (const_int 0))) + (set (match_operand:GPR 0 "register_operand" "=d") + (neg:GPR (abs:GPR (match_dup 1))))] + "s390_match_ccmode (insn, CCAmode)" + "ln<g>r\t%0,%1" + [(set_attr "op_type" "RR<E>") + (set_attr "z10prop" "z10_c")]) + +; lnr, lngr +(define_insn "*negabs<mode>2_cconly" + [(set (reg CC_REGNUM) + (compare (neg:GPR (abs:GPR (match_operand:GPR 1 "register_operand" "d"))) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=d"))] + "s390_match_ccmode (insn, CCAmode)" + "ln<g>r\t%0,%1" + [(set_attr "op_type" "RR<E>") + (set_attr "z10prop" "z10_c")]) + +; lnr, lngr +(define_insn "*negabs<mode>2" + [(set (match_operand:GPR 0 "register_operand" "=d") + (neg:GPR (abs:GPR (match_operand:GPR 1 "register_operand" "d")))) + (clobber (reg:CC CC_REGNUM))] + "" + "ln<g>r\t%0,%1" + [(set_attr "op_type" "RR<E>") + (set_attr "z10prop" "z10_c")]) + +; +; Floating point +; + +; lnxbr, lndbr, lnebr +(define_insn "*negabs<mode>2_cc" + [(set (reg CC_REGNUM) + (compare (neg:BFP (abs:BFP (match_operand:BFP 1 "register_operand" "f"))) + (match_operand:BFP 2 "const0_operand" ""))) + (set (match_operand:BFP 0 "register_operand" "=f") + (neg:BFP (abs:BFP (match_dup 1))))] + "s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT" + "ln<xde>br\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + +; lnxbr, lndbr, lnebr +(define_insn "*negabs<mode>2_cconly" + [(set (reg CC_REGNUM) + (compare (neg:BFP (abs:BFP (match_operand:BFP 1 "register_operand" "f"))) + (match_operand:BFP 2 "const0_operand" ""))) + (clobber (match_scratch:BFP 0 "=f"))] + "s390_match_ccmode (insn, CCSmode) && TARGET_HARD_FLOAT" + "ln<xde>br\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + +; lndfr +(define_insn "*negabs<mode>2_nocc" + [(set (match_operand:FP 0 "register_operand" "=f") + (neg:FP (abs:FP (match_operand:BFP 1 "register_operand" "<fT0>"))))] + "TARGET_DFP" + "lndfr\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + +; lnxbr, lndbr, lnebr +(define_insn "*negabs<mode>2" + [(set (match_operand:BFP 0 "register_operand" "=f") + (neg:BFP (abs:BFP (match_operand:BFP 1 "register_operand" "f")))) + (clobber (reg:CC CC_REGNUM))] + "TARGET_HARD_FLOAT" + "ln<xde>br\t%0,%1" + [(set_attr "op_type" "RRE") + (set_attr "type" "fsimp<mode>")]) + +;; +;;- Square root instructions. +;; + +; +; sqrt(df|sf)2 instruction pattern(s). +; + +; sqxbr, sqdbr, sqebr, sqdb, sqeb +(define_insn "sqrt<mode>2" + [(set (match_operand:BFP 0 "register_operand" "=f,f") + (sqrt:BFP (match_operand:BFP 1 "general_operand" "f,<Rf>")))] + "TARGET_HARD_FLOAT" + "@ + sq<xde>br\t%0,%1 + sq<xde>b\t%0,%1" + [(set_attr "op_type" "RRE,RXE") + (set_attr "type" "fsqrt<mode>")]) + + +;; +;;- One complement instructions. +;; + +; +; one_cmpl(di|si|hi|qi)2 instruction pattern(s). +; + +(define_expand "one_cmpl<mode>2" + [(parallel + [(set (match_operand:INT 0 "register_operand" "") + (xor:INT (match_operand:INT 1 "register_operand" "") + (const_int -1))) + (clobber (reg:CC CC_REGNUM))])] + "" + "") + + +;; +;; Find leftmost bit instructions. +;; + +(define_expand "clzdi2" + [(set (match_operand:DI 0 "register_operand" "=d") + (clz:DI (match_operand:DI 1 "register_operand" "d")))] + "TARGET_EXTIMM && TARGET_ZARCH" +{ + rtx insn, clz_equal; + rtx wide_reg = gen_reg_rtx (TImode); + rtx msb = gen_rtx_CONST_INT (DImode, (unsigned HOST_WIDE_INT) 1 << 63); + + clz_equal = gen_rtx_CLZ (DImode, operands[1]); + + emit_insn (gen_clztidi2 (wide_reg, operands[1], msb)); + + insn = emit_move_insn (operands[0], gen_highpart (DImode, wide_reg)); + set_unique_reg_note (insn, REG_EQUAL, clz_equal); + + DONE; +}) + +(define_insn "clztidi2" + [(set (match_operand:TI 0 "register_operand" "=d") + (ior:TI + (ashift:TI + (zero_extend:TI + (xor:DI (match_operand:DI 1 "register_operand" "d") + (lshiftrt (match_operand:DI 2 "const_int_operand" "") + (subreg:SI (clz:DI (match_dup 1)) 4)))) + + (const_int 64)) + (zero_extend:TI (clz:DI (match_dup 1))))) + (clobber (reg:CC CC_REGNUM))] + "(unsigned HOST_WIDE_INT) INTVAL (operands[2]) + == (unsigned HOST_WIDE_INT) 1 << 63 + && TARGET_EXTIMM && TARGET_ZARCH" + "flogr\t%0,%1" + [(set_attr "op_type" "RRE")]) + + +;; +;;- Rotate instructions. +;; + +; +; rotl(di|si)3 instruction pattern(s). +; + +; rll, rllg +(define_insn "rotl<mode>3" + [(set (match_operand:GPR 0 "register_operand" "=d") + (rotate:GPR (match_operand:GPR 1 "register_operand" "d") + (match_operand:SI 2 "shift_count_or_setmem_operand" "Y")))] + "TARGET_CPU_ZARCH" + "rll<g>\t%0,%1,%Y2" + [(set_attr "op_type" "RSE") + (set_attr "atype" "reg") + (set_attr "z10prop" "z10_super_E1")]) + +; rll, rllg +(define_insn "*rotl<mode>3_and" + [(set (match_operand:GPR 0 "register_operand" "=d") + (rotate:GPR (match_operand:GPR 1 "register_operand" "d") + (and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") + (match_operand:SI 3 "const_int_operand" "n"))))] + "TARGET_CPU_ZARCH && (INTVAL (operands[3]) & 63) == 63" + "rll<g>\t%0,%1,%Y2" + [(set_attr "op_type" "RSE") + (set_attr "atype" "reg") + (set_attr "z10prop" "z10_super_E1")]) + + +;; +;;- Shift instructions. +;; + +; +; (ashl|lshr)(di|si)3 instruction pattern(s). +; Left shifts and logical right shifts + +(define_expand "<shift><mode>3" + [(set (match_operand:DSI 0 "register_operand" "") + (SHIFT:DSI (match_operand:DSI 1 "register_operand" "") + (match_operand:SI 2 "shift_count_or_setmem_operand" "")))] + "" + "") + +; sldl, srdl +(define_insn "*<shift>di3_31" + [(set (match_operand:DI 0 "register_operand" "=d") + (SHIFT:DI (match_operand:DI 1 "register_operand" "0") + (match_operand:SI 2 "shift_count_or_setmem_operand" "Y")))] + "!TARGET_ZARCH" + "s<lr>dl\t%0,%Y2" + [(set_attr "op_type" "RS") + (set_attr "atype" "reg") + (set_attr "z196prop" "z196_cracked")]) + +; sll, srl, sllg, srlg, sllk, srlk +(define_insn "*<shift><mode>3" + [(set (match_operand:GPR 0 "register_operand" "=d,d") + (SHIFT:GPR (match_operand:GPR 1 "register_operand" "<d0>,d") + (match_operand:SI 2 "shift_count_or_setmem_operand" "Y,Y")))] + "" + "@ + s<lr>l<g>\t%0,<1>%Y2 + s<lr>l<gk>\t%0,%1,%Y2" + [(set_attr "op_type" "RS<E>,RSY") + (set_attr "atype" "reg,reg") + (set_attr "cpu_facility" "*,z196") + (set_attr "z10prop" "z10_super_E1,*")]) + +; sldl, srdl +(define_insn "*<shift>di3_31_and" + [(set (match_operand:DI 0 "register_operand" "=d") + (SHIFT:DI (match_operand:DI 1 "register_operand" "0") + (and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") + (match_operand:SI 3 "const_int_operand" "n"))))] + "!TARGET_ZARCH && (INTVAL (operands[3]) & 63) == 63" + "s<lr>dl\t%0,%Y2" + [(set_attr "op_type" "RS") + (set_attr "atype" "reg")]) + +; sll, srl, sllg, srlg, sllk, srlk +(define_insn "*<shift><mode>3_and" + [(set (match_operand:GPR 0 "register_operand" "=d,d") + (SHIFT:GPR (match_operand:GPR 1 "register_operand" "<d0>,d") + (and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y,Y") + (match_operand:SI 3 "const_int_operand" "n,n"))))] + "(INTVAL (operands[3]) & 63) == 63" + "@ + s<lr>l<g>\t%0,<1>%Y2 + s<lr>l<gk>\t%0,%1,%Y2" + [(set_attr "op_type" "RS<E>,RSY") + (set_attr "atype" "reg,reg") + (set_attr "cpu_facility" "*,z196") + (set_attr "z10prop" "z10_super_E1,*")]) + +; +; ashr(di|si)3 instruction pattern(s). +; Arithmetic right shifts + +(define_expand "ashr<mode>3" + [(parallel + [(set (match_operand:DSI 0 "register_operand" "") + (ashiftrt:DSI (match_operand:DSI 1 "register_operand" "") + (match_operand:SI 2 "shift_count_or_setmem_operand" ""))) + (clobber (reg:CC CC_REGNUM))])] + "" + "") + +(define_insn "*ashrdi3_cc_31" + [(set (reg CC_REGNUM) + (compare (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") + (match_operand:SI 2 "shift_count_or_setmem_operand" "Y")) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d") + (ashiftrt:DI (match_dup 1) (match_dup 2)))] + "!TARGET_ZARCH && s390_match_ccmode(insn, CCSmode)" + "srda\t%0,%Y2" + [(set_attr "op_type" "RS") + (set_attr "atype" "reg")]) + +(define_insn "*ashrdi3_cconly_31" + [(set (reg CC_REGNUM) + (compare (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") + (match_operand:SI 2 "shift_count_or_setmem_operand" "Y")) + (const_int 0))) + (clobber (match_scratch:DI 0 "=d"))] + "!TARGET_ZARCH && s390_match_ccmode(insn, CCSmode)" + "srda\t%0,%Y2" + [(set_attr "op_type" "RS") + (set_attr "atype" "reg")]) + +(define_insn "*ashrdi3_31" + [(set (match_operand:DI 0 "register_operand" "=d") + (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") + (match_operand:SI 2 "shift_count_or_setmem_operand" "Y"))) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH" + "srda\t%0,%Y2" + [(set_attr "op_type" "RS") + (set_attr "atype" "reg")]) + +; sra, srag, srak +(define_insn "*ashr<mode>3_cc" + [(set (reg CC_REGNUM) + (compare (ashiftrt:GPR (match_operand:GPR 1 "register_operand" "<d0>,d") + (match_operand:SI 2 "shift_count_or_setmem_operand" "Y,Y")) + (const_int 0))) + (set (match_operand:GPR 0 "register_operand" "=d,d") + (ashiftrt:GPR (match_dup 1) (match_dup 2)))] + "s390_match_ccmode(insn, CCSmode)" + "@ + sra<g>\t%0,<1>%Y2 + sra<gk>\t%0,%1,%Y2" + [(set_attr "op_type" "RS<E>,RSY") + (set_attr "atype" "reg,reg") + (set_attr "cpu_facility" "*,z196") + (set_attr "z10prop" "z10_super_E1,*")]) + +; sra, srag, srak +(define_insn "*ashr<mode>3_cconly" + [(set (reg CC_REGNUM) + (compare (ashiftrt:GPR (match_operand:GPR 1 "register_operand" "<d0>,d") + (match_operand:SI 2 "shift_count_or_setmem_operand" "Y,Y")) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=d,d"))] + "s390_match_ccmode(insn, CCSmode)" + "@ + sra<g>\t%0,<1>%Y2 + sra<gk>\t%0,%1,%Y2" + [(set_attr "op_type" "RS<E>,RSY") + (set_attr "atype" "reg,reg") + (set_attr "cpu_facility" "*,z196") + (set_attr "z10prop" "z10_super_E1,*")]) + +; sra, srag +(define_insn "*ashr<mode>3" + [(set (match_operand:GPR 0 "register_operand" "=d,d") + (ashiftrt:GPR (match_operand:GPR 1 "register_operand" "<d0>,d") + (match_operand:SI 2 "shift_count_or_setmem_operand" "Y,Y"))) + (clobber (reg:CC CC_REGNUM))] + "" + "@ + sra<g>\t%0,<1>%Y2 + sra<gk>\t%0,%1,%Y2" + [(set_attr "op_type" "RS<E>,RSY") + (set_attr "atype" "reg,reg") + (set_attr "cpu_facility" "*,z196") + (set_attr "z10prop" "z10_super_E1,*")]) + + +; shift pattern with implicit ANDs + +(define_insn "*ashrdi3_cc_31_and" + [(set (reg CC_REGNUM) + (compare (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") + (and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") + (match_operand:SI 3 "const_int_operand" "n"))) + (const_int 0))) + (set (match_operand:DI 0 "register_operand" "=d") + (ashiftrt:DI (match_dup 1) (and:SI (match_dup 2) (match_dup 3))))] + "!TARGET_ZARCH && s390_match_ccmode(insn, CCSmode) + && (INTVAL (operands[3]) & 63) == 63" + "srda\t%0,%Y2" + [(set_attr "op_type" "RS") + (set_attr "atype" "reg")]) + +(define_insn "*ashrdi3_cconly_31_and" + [(set (reg CC_REGNUM) + (compare (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") + (and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") + (match_operand:SI 3 "const_int_operand" "n"))) + (const_int 0))) + (clobber (match_scratch:DI 0 "=d"))] + "!TARGET_ZARCH && s390_match_ccmode(insn, CCSmode) + && (INTVAL (operands[3]) & 63) == 63" + "srda\t%0,%Y2" + [(set_attr "op_type" "RS") + (set_attr "atype" "reg")]) + +(define_insn "*ashrdi3_31_and" + [(set (match_operand:DI 0 "register_operand" "=d") + (ashiftrt:DI (match_operand:DI 1 "register_operand" "0") + (and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y") + (match_operand:SI 3 "const_int_operand" "n")))) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH && (INTVAL (operands[3]) & 63) == 63" + "srda\t%0,%Y2" + [(set_attr "op_type" "RS") + (set_attr "atype" "reg")]) + +; sra, srag, srak +(define_insn "*ashr<mode>3_cc_and" + [(set (reg CC_REGNUM) + (compare (ashiftrt:GPR (match_operand:GPR 1 "register_operand" "<d0>,d") + (and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y,Y") + (match_operand:SI 3 "const_int_operand" "n,n"))) + (const_int 0))) + (set (match_operand:GPR 0 "register_operand" "=d,d") + (ashiftrt:GPR (match_dup 1) (and:SI (match_dup 2) (match_dup 3))))] + "s390_match_ccmode(insn, CCSmode) && (INTVAL (operands[3]) & 63) == 63" + "@ + sra<g>\t%0,<1>%Y2 + sra<gk>\t%0,%1,%Y2" + [(set_attr "op_type" "RS<E>,RSY") + (set_attr "atype" "reg,reg") + (set_attr "cpu_facility" "*,z196") + (set_attr "z10prop" "z10_super_E1,*")]) + +; sra, srag, srak +(define_insn "*ashr<mode>3_cconly_and" + [(set (reg CC_REGNUM) + (compare (ashiftrt:GPR (match_operand:GPR 1 "register_operand" "<d0>,d") + (and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y,Y") + (match_operand:SI 3 "const_int_operand" "n,n"))) + (const_int 0))) + (clobber (match_scratch:GPR 0 "=d,d"))] + "s390_match_ccmode(insn, CCSmode) && (INTVAL (operands[3]) & 63) == 63" + "@ + sra<g>\t%0,<1>%Y2 + sra<gk>\t%0,%1,%Y2" + [(set_attr "op_type" "RS<E>,RSY") + (set_attr "atype" "reg,reg") + (set_attr "cpu_facility" "*,z196") + (set_attr "z10prop" "z10_super_E1,*")]) + +; sra, srag, srak +(define_insn "*ashr<mode>3_and" + [(set (match_operand:GPR 0 "register_operand" "=d,d") + (ashiftrt:GPR (match_operand:GPR 1 "register_operand" "<d0>,d") + (and:SI (match_operand:SI 2 "shift_count_or_setmem_operand" "Y,Y") + (match_operand:SI 3 "const_int_operand" "n,n")))) + (clobber (reg:CC CC_REGNUM))] + "(INTVAL (operands[3]) & 63) == 63" + "@ + sra<g>\t%0,<1>%Y2 + sra<gk>\t%0,%1,%Y2" + [(set_attr "op_type" "RS<E>,RSY") + (set_attr "atype" "reg,reg") + (set_attr "cpu_facility" "*,z196") + (set_attr "z10prop" "z10_super_E1,*")]) + + +;; +;; Branch instruction patterns. +;; + +(define_expand "cbranch<mode>4" + [(set (pc) + (if_then_else (match_operator 0 "comparison_operator" + [(match_operand:GPR 1 "register_operand" "") + (match_operand:GPR 2 "general_operand" "")]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "" + "s390_emit_jump (operands[3], + s390_emit_compare (GET_CODE (operands[0]), operands[1], operands[2])); + DONE;") + +(define_expand "cbranch<mode>4" + [(set (pc) + (if_then_else (match_operator 0 "comparison_operator" + [(match_operand:FP 1 "register_operand" "") + (match_operand:FP 2 "general_operand" "")]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "TARGET_HARD_FLOAT" + "s390_emit_jump (operands[3], + s390_emit_compare (GET_CODE (operands[0]), operands[1], operands[2])); + DONE;") + +(define_expand "cbranchcc4" + [(set (pc) + (if_then_else (match_operator 0 "s390_eqne_operator" + [(match_operand 1 "cc_reg_operand" "") + (match_operand 2 "const0_operand" "")]) + (label_ref (match_operand 3 "" "")) + (pc)))] + "TARGET_HARD_FLOAT" + "s390_emit_jump (operands[3], + s390_emit_compare (GET_CODE (operands[0]), operands[1], operands[2])); + DONE;") + + + +;; +;;- Conditional jump instructions. +;; + +(define_insn "*cjump_64" + [(set (pc) + (if_then_else + (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "TARGET_CPU_ZARCH" +{ + if (get_attr_length (insn) == 4) + return "j%C1\t%l0"; + else + return "jg%C1\t%l0"; +} + [(set_attr "op_type" "RI") + (set_attr "type" "branch") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 6)))]) + +(define_insn "*cjump_31" + [(set (pc) + (if_then_else + (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) + (label_ref (match_operand 0 "" "")) + (pc)))] + "!TARGET_CPU_ZARCH" +{ + gcc_assert (get_attr_length (insn) == 4); + return "j%C1\t%l0"; +} + [(set_attr "op_type" "RI") + (set_attr "type" "branch") + (set (attr "length") + (if_then_else (eq (symbol_ref "flag_pic") (const_int 0)) + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 6)) + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 8))))]) + +(define_insn "*cjump_long" + [(set (pc) + (if_then_else + (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) + (match_operand 0 "address_operand" "ZQZR") + (pc)))] + "" +{ + if (get_attr_op_type (insn) == OP_TYPE_RR) + return "b%C1r\t%0"; + else + return "b%C1\t%a0"; +} + [(set (attr "op_type") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "RR") (const_string "RX"))) + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + + +;; +;;- Negated conditional jump instructions. +;; + +(define_insn "*icjump_64" + [(set (pc) + (if_then_else + (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) + (pc) + (label_ref (match_operand 0 "" ""))))] + "TARGET_CPU_ZARCH" +{ + if (get_attr_length (insn) == 4) + return "j%D1\t%l0"; + else + return "jg%D1\t%l0"; +} + [(set_attr "op_type" "RI") + (set_attr "type" "branch") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 6)))]) + +(define_insn "*icjump_31" + [(set (pc) + (if_then_else + (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) + (pc) + (label_ref (match_operand 0 "" ""))))] + "!TARGET_CPU_ZARCH" +{ + gcc_assert (get_attr_length (insn) == 4); + return "j%D1\t%l0"; +} + [(set_attr "op_type" "RI") + (set_attr "type" "branch") + (set (attr "length") + (if_then_else (eq (symbol_ref "flag_pic") (const_int 0)) + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 6)) + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 8))))]) + +(define_insn "*icjump_long" + [(set (pc) + (if_then_else + (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)]) + (pc) + (match_operand 0 "address_operand" "ZQZR")))] + "" +{ + if (get_attr_op_type (insn) == OP_TYPE_RR) + return "b%D1r\t%0"; + else + return "b%D1\t%a0"; +} + [(set (attr "op_type") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "RR") (const_string "RX"))) + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + +;; +;;- Trap instructions. +;; + +(define_insn "trap" + [(trap_if (const_int 1) (const_int 0))] + "" + "j\t.+2" + [(set_attr "op_type" "RI") + (set_attr "type" "branch")]) + +(define_expand "ctrap<mode>4" + [(trap_if (match_operator 0 "comparison_operator" + [(match_operand:GPR 1 "register_operand" "") + (match_operand:GPR 2 "general_operand" "")]) + (match_operand 3 "const0_operand" ""))] + "" + { + rtx cond = s390_emit_compare (GET_CODE (operands[0]), + operands[1], operands[2]); + emit_insn (gen_condtrap (cond, XEXP (cond, 0))); + DONE; + }) + +(define_expand "ctrap<mode>4" + [(trap_if (match_operator 0 "comparison_operator" + [(match_operand:FP 1 "register_operand" "") + (match_operand:FP 2 "general_operand" "")]) + (match_operand 3 "const0_operand" ""))] + "" + { + rtx cond = s390_emit_compare (GET_CODE (operands[0]), + operands[1], operands[2]); + emit_insn (gen_condtrap (cond, XEXP (cond, 0))); + DONE; + }) + +(define_insn "condtrap" + [(trap_if (match_operator 0 "s390_comparison" + [(match_operand 1 "cc_reg_operand" "c") + (const_int 0)]) + (const_int 0))] + "" + "j%C0\t.+2"; + [(set_attr "op_type" "RI") + (set_attr "type" "branch")]) + +; crt, cgrt, cit, cgit +(define_insn "*cmp_and_trap_signed_int<mode>" + [(trap_if (match_operator 0 "s390_signed_integer_comparison" + [(match_operand:GPR 1 "register_operand" "d,d") + (match_operand:GPR 2 "nonmemory_operand" "d,K")]) + (const_int 0))] + "TARGET_Z10" + "@ + c<g>rt%C0\t%1,%2 + c<g>it%C0\t%1,%h2" + [(set_attr "op_type" "RRF,RIE") + (set_attr "type" "branch") + (set_attr "z10prop" "z10_super_c,z10_super")]) + +; clrt, clgrt, clfit, clgit +(define_insn "*cmp_and_trap_unsigned_int<mode>" + [(trap_if (match_operator 0 "s390_unsigned_integer_comparison" + [(match_operand:GPR 1 "register_operand" "d,d") + (match_operand:GPR 2 "nonmemory_operand" "d,D")]) + (const_int 0))] + "TARGET_Z10" + "@ + cl<g>rt%C0\t%1,%2 + cl<gf>it%C0\t%1,%x2" + [(set_attr "op_type" "RRF,RIE") + (set_attr "type" "branch") + (set_attr "z10prop" "z10_super_c,z10_super")]) + +;; +;;- Loop instructions. +;; +;; This is all complicated by the fact that since this is a jump insn +;; we must handle our own output reloads. + +;; branch on index + +; This splitter will be matched by combine and has to add the 2 moves +; necessary to load the compare and the increment values into a +; register pair as needed by brxle. + +(define_insn_and_split "*brx_stage1_<GPR:mode>" + [(set (pc) + (if_then_else + (match_operator 6 "s390_brx_operator" + [(plus:GPR (match_operand:GPR 1 "register_operand" "") + (match_operand:GPR 2 "general_operand" "")) + (match_operand:GPR 3 "register_operand" "")]) + (label_ref (match_operand 0 "" "")) + (pc))) + (set (match_operand:GPR 4 "nonimmediate_operand" "") + (plus:GPR (match_dup 1) (match_dup 2))) + (clobber (match_scratch:GPR 5 ""))] + "TARGET_CPU_ZARCH" + "#" + "!reload_completed && !reload_in_progress" + [(set (match_dup 7) (match_dup 2)) ; the increment + (set (match_dup 8) (match_dup 3)) ; the comparison value + (parallel [(set (pc) + (if_then_else + (match_op_dup 6 + [(plus:GPR (match_dup 1) (match_dup 7)) + (match_dup 8)]) + (label_ref (match_dup 0)) + (pc))) + (set (match_dup 4) + (plus:GPR (match_dup 1) (match_dup 7))) + (clobber (match_dup 5)) + (clobber (reg:CC CC_REGNUM))])] + { + rtx dreg = gen_reg_rtx (word_mode == DImode ? TImode : DImode); + operands[7] = gen_lowpart (<GPR:MODE>mode, + gen_highpart (word_mode, dreg)); + operands[8] = gen_lowpart (<GPR:MODE>mode, + gen_lowpart (word_mode, dreg)); + }) + +; brxlg, brxhg + +(define_insn_and_split "*brxg_64bit" + [(set (pc) + (if_then_else + (match_operator 5 "s390_brx_operator" + [(plus:DI (match_operand:DI 1 "register_operand" "d,d,d") + (subreg:DI (match_operand:TI 2 "register_operand" "d,d,d") 0)) + (subreg:DI (match_dup 2) 8)]) + (label_ref (match_operand 0 "" "")) + (pc))) + (set (match_operand:DI 3 "nonimmediate_operand" "=1,?X,?X") + (plus:DI (match_dup 1) + (subreg:DI (match_dup 2) 0))) + (clobber (match_scratch:DI 4 "=X,&1,&?d")) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" +{ + if (which_alternative != 0) + return "#"; + else if (get_attr_length (insn) == 6) + return "brx%E5g\t%1,%2,%l0"; + else + return "agr\t%1,%2\;cgr\t%1,%M2\;jg%C5\t%l0"; +} + "&& reload_completed + && (!REG_P (operands[3]) + || !rtx_equal_p (operands[1], operands[3]))" + [(set (match_dup 4) (match_dup 1)) + (parallel [(set (match_dup 4) (plus:DI (match_dup 4) (subreg:DI (match_dup 2) 0))) + (clobber (reg:CC CC_REGNUM))]) + (set (reg:CCS CC_REGNUM) (compare:CCS (match_dup 4) (subreg:DI (match_dup 2) 8))) + (set (match_dup 3) (match_dup 4)) + (set (pc) (if_then_else (match_op_dup 5 [(reg:CCS CC_REGNUM) (const_int 0)]) + (label_ref (match_dup 0)) + (pc)))] + "" + [(set_attr "op_type" "RIE") + (set_attr "type" "branch") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 6) (const_int 16)))]) + +; brxle, brxh + +(define_insn_and_split "*brx_64bit" + [(set (pc) + (if_then_else + (match_operator 5 "s390_brx_operator" + [(plus:SI (match_operand:SI 1 "register_operand" "d,d,d") + (subreg:SI (match_operand:TI 2 "register_operand" "d,d,d") 4)) + (subreg:SI (match_dup 2) 12)]) + (label_ref (match_operand 0 "" "")) + (pc))) + (set (match_operand:SI 3 "nonimmediate_operand" "=1,?X,?X") + (plus:SI (match_dup 1) + (subreg:SI (match_dup 2) 4))) + (clobber (match_scratch:SI 4 "=X,&1,&?d")) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" +{ + if (which_alternative != 0) + return "#"; + else if (get_attr_length (insn) == 6) + return "brx%C5\t%1,%2,%l0"; + else + return "ar\t%1,%2\;cr\t%1,%M2\;jg%C5\t%l0"; +} + "&& reload_completed + && (!REG_P (operands[3]) + || !rtx_equal_p (operands[1], operands[3]))" + [(set (match_dup 4) (match_dup 1)) + (parallel [(set (match_dup 4) (plus:SI (match_dup 4) (subreg:SI (match_dup 2) 4))) + (clobber (reg:CC CC_REGNUM))]) + (set (reg:CCS CC_REGNUM) (compare:CCS (match_dup 4) (subreg:SI (match_dup 2) 12))) + (set (match_dup 3) (match_dup 4)) + (set (pc) (if_then_else (match_op_dup 5 [(reg:CCS CC_REGNUM) (const_int 0)]) + (label_ref (match_dup 0)) + (pc)))] + "" + [(set_attr "op_type" "RSI") + (set_attr "type" "branch") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 6) (const_int 14)))]) + +; brxle, brxh + +(define_insn_and_split "*brx_31bit" + [(set (pc) + (if_then_else + (match_operator 5 "s390_brx_operator" + [(plus:SI (match_operand:SI 1 "register_operand" "d,d,d") + (subreg:SI (match_operand:DI 2 "register_operand" "d,d,d") 0)) + (subreg:SI (match_dup 2) 4)]) + (label_ref (match_operand 0 "" "")) + (pc))) + (set (match_operand:SI 3 "nonimmediate_operand" "=1,?X,?X") + (plus:SI (match_dup 1) + (subreg:SI (match_dup 2) 0))) + (clobber (match_scratch:SI 4 "=X,&1,&?d")) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_ZARCH && TARGET_CPU_ZARCH" +{ + if (which_alternative != 0) + return "#"; + else if (get_attr_length (insn) == 6) + return "brx%C5\t%1,%2,%l0"; + else + return "ar\t%1,%2\;cr\t%1,%M2\;jg%C5\t%l0"; +} + "&& reload_completed + && (!REG_P (operands[3]) + || !rtx_equal_p (operands[1], operands[3]))" + [(set (match_dup 4) (match_dup 1)) + (parallel [(set (match_dup 4) (plus:SI (match_dup 4) (subreg:SI (match_dup 2) 0))) + (clobber (reg:CC CC_REGNUM))]) + (set (reg:CCS CC_REGNUM) (compare:CCS (match_dup 4) (subreg:SI (match_dup 2) 4))) + (set (match_dup 3) (match_dup 4)) + (set (pc) (if_then_else (match_op_dup 5 [(reg:CCS CC_REGNUM) (const_int 0)]) + (label_ref (match_dup 0)) + (pc)))] + "" + [(set_attr "op_type" "RSI") + (set_attr "type" "branch") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 6) (const_int 14)))]) + + +;; branch on count + +(define_expand "doloop_end" + [(use (match_operand 0 "" "")) ; loop pseudo + (use (match_operand 1 "" "")) ; iterations; zero if unknown + (use (match_operand 2 "" "")) ; max iterations + (use (match_operand 3 "" "")) ; loop level + (use (match_operand 4 "" ""))] ; label + "" +{ + if (GET_MODE (operands[0]) == SImode && !TARGET_CPU_ZARCH) + emit_jump_insn (gen_doloop_si31 (operands[4], operands[0], operands[0])); + else if (GET_MODE (operands[0]) == SImode && TARGET_CPU_ZARCH) + emit_jump_insn (gen_doloop_si64 (operands[4], operands[0], operands[0])); + else if (GET_MODE (operands[0]) == DImode && TARGET_ZARCH) + emit_jump_insn (gen_doloop_di (operands[4], operands[0], operands[0])); + else + FAIL; + + DONE; +}) + +(define_insn_and_split "doloop_si64" + [(set (pc) + (if_then_else + (ne (match_operand:SI 1 "register_operand" "d,d,d") + (const_int 1)) + (label_ref (match_operand 0 "" "")) + (pc))) + (set (match_operand:SI 2 "nonimmediate_operand" "=1,?X,?X") + (plus:SI (match_dup 1) (const_int -1))) + (clobber (match_scratch:SI 3 "=X,&1,&?d")) + (clobber (reg:CC CC_REGNUM))] + "TARGET_CPU_ZARCH" +{ + if (which_alternative != 0) + return "#"; + else if (get_attr_length (insn) == 4) + return "brct\t%1,%l0"; + else + return "ahi\t%1,-1\;jgne\t%l0"; +} + "&& reload_completed + && (! REG_P (operands[2]) + || ! rtx_equal_p (operands[1], operands[2]))" + [(set (match_dup 3) (match_dup 1)) + (parallel [(set (reg:CCAN CC_REGNUM) + (compare:CCAN (plus:SI (match_dup 3) (const_int -1)) + (const_int 0))) + (set (match_dup 3) (plus:SI (match_dup 3) (const_int -1)))]) + (set (match_dup 2) (match_dup 3)) + (set (pc) (if_then_else (ne (reg:CCAN CC_REGNUM) (const_int 0)) + (label_ref (match_dup 0)) + (pc)))] + "" + [(set_attr "op_type" "RI") + ; Strictly speaking, the z10 properties are valid for brct only, however, it does not + ; hurt us in the (rare) case of ahi. + (set_attr "z10prop" "z10_super_E1") + (set_attr "type" "branch") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 10)))]) + +(define_insn_and_split "doloop_si31" + [(set (pc) + (if_then_else + (ne (match_operand:SI 1 "register_operand" "d,d,d") + (const_int 1)) + (label_ref (match_operand 0 "" "")) + (pc))) + (set (match_operand:SI 2 "nonimmediate_operand" "=1,?X,?X") + (plus:SI (match_dup 1) (const_int -1))) + (clobber (match_scratch:SI 3 "=X,&1,&?d")) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_CPU_ZARCH" +{ + if (which_alternative != 0) + return "#"; + else if (get_attr_length (insn) == 4) + return "brct\t%1,%l0"; + else + gcc_unreachable (); +} + "&& reload_completed + && (! REG_P (operands[2]) + || ! rtx_equal_p (operands[1], operands[2]))" + [(set (match_dup 3) (match_dup 1)) + (parallel [(set (reg:CCAN CC_REGNUM) + (compare:CCAN (plus:SI (match_dup 3) (const_int -1)) + (const_int 0))) + (set (match_dup 3) (plus:SI (match_dup 3) (const_int -1)))]) + (set (match_dup 2) (match_dup 3)) + (set (pc) (if_then_else (ne (reg:CCAN CC_REGNUM) (const_int 0)) + (label_ref (match_dup 0)) + (pc)))] + "" + [(set_attr "op_type" "RI") + ; Strictly speaking, the z10 properties are valid for brct only, however, it does not + ; hurt us in the (rare) case of ahi. + (set_attr "z10prop" "z10_super_E1") + (set_attr "type" "branch") + (set (attr "length") + (if_then_else (eq (symbol_ref "flag_pic") (const_int 0)) + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 6)) + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 8))))]) + +(define_insn "*doloop_si_long" + [(set (pc) + (if_then_else + (ne (match_operand:SI 1 "register_operand" "d") + (const_int 1)) + (match_operand 0 "address_operand" "ZQZR") + (pc))) + (set (match_operand:SI 2 "register_operand" "=1") + (plus:SI (match_dup 1) (const_int -1))) + (clobber (match_scratch:SI 3 "=X")) + (clobber (reg:CC CC_REGNUM))] + "!TARGET_CPU_ZARCH" +{ + if (get_attr_op_type (insn) == OP_TYPE_RR) + return "bctr\t%1,%0"; + else + return "bct\t%1,%a0"; +} + [(set (attr "op_type") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "RR") (const_string "RX"))) + (set_attr "type" "branch") + (set_attr "atype" "agen") + (set_attr "z10prop" "z10_c") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn_and_split "doloop_di" + [(set (pc) + (if_then_else + (ne (match_operand:DI 1 "register_operand" "d,d,d") + (const_int 1)) + (label_ref (match_operand 0 "" "")) + (pc))) + (set (match_operand:DI 2 "nonimmediate_operand" "=1,?X,?X") + (plus:DI (match_dup 1) (const_int -1))) + (clobber (match_scratch:DI 3 "=X,&1,&?d")) + (clobber (reg:CC CC_REGNUM))] + "TARGET_ZARCH" +{ + if (which_alternative != 0) + return "#"; + else if (get_attr_length (insn) == 4) + return "brctg\t%1,%l0"; + else + return "aghi\t%1,-1\;jgne\t%l0"; +} + "&& reload_completed + && (! REG_P (operands[2]) + || ! rtx_equal_p (operands[1], operands[2]))" + [(set (match_dup 3) (match_dup 1)) + (parallel [(set (reg:CCAN CC_REGNUM) + (compare:CCAN (plus:DI (match_dup 3) (const_int -1)) + (const_int 0))) + (set (match_dup 3) (plus:DI (match_dup 3) (const_int -1)))]) + (set (match_dup 2) (match_dup 3)) + (set (pc) (if_then_else (ne (reg:CCAN CC_REGNUM) (const_int 0)) + (label_ref (match_dup 0)) + (pc)))] + "" + [(set_attr "op_type" "RI") + ; Strictly speaking, the z10 properties are valid for brct only, however, it does not + ; hurt us in the (rare) case of ahi. + (set_attr "z10prop" "z10_super_E1") + (set_attr "type" "branch") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 10)))]) + +;; +;;- Unconditional jump instructions. +;; + +; +; jump instruction pattern(s). +; + +(define_expand "jump" + [(match_operand 0 "" "")] + "" + "s390_emit_jump (operands[0], NULL_RTX); DONE;") + +(define_insn "*jump64" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "TARGET_CPU_ZARCH" +{ + if (get_attr_length (insn) == 4) + return "j\t%l0"; + else + return "jg\t%l0"; +} + [(set_attr "op_type" "RI") + (set_attr "type" "branch") + (set (attr "length") + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 6)))]) + +(define_insn "*jump31" + [(set (pc) (label_ref (match_operand 0 "" "")))] + "!TARGET_CPU_ZARCH" +{ + gcc_assert (get_attr_length (insn) == 4); + return "j\t%l0"; +} + [(set_attr "op_type" "RI") + (set_attr "type" "branch") + (set (attr "length") + (if_then_else (eq (symbol_ref "flag_pic") (const_int 0)) + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 6)) + (if_then_else (lt (abs (minus (pc) (match_dup 0))) (const_int 60000)) + (const_int 4) (const_int 8))))]) + +; +; indirect-jump instruction pattern(s). +; + +(define_insn "indirect_jump" + [(set (pc) (match_operand 0 "address_operand" "ZQZR"))] + "" +{ + if (get_attr_op_type (insn) == OP_TYPE_RR) + return "br\t%0"; + else + return "b\t%a0"; +} + [(set (attr "op_type") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "RR") (const_string "RX"))) + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + +; +; casesi instruction pattern(s). +; + +(define_insn "casesi_jump" + [(set (pc) (match_operand 0 "address_operand" "ZQZR")) + (use (label_ref (match_operand 1 "" "")))] + "" +{ + if (get_attr_op_type (insn) == OP_TYPE_RR) + return "br\t%0"; + else + return "b\t%a0"; +} + [(set (attr "op_type") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "RR") (const_string "RX"))) + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + +(define_expand "casesi" + [(match_operand:SI 0 "general_operand" "") + (match_operand:SI 1 "general_operand" "") + (match_operand:SI 2 "general_operand" "") + (label_ref (match_operand 3 "" "")) + (label_ref (match_operand 4 "" ""))] + "" +{ + rtx index = gen_reg_rtx (SImode); + rtx base = gen_reg_rtx (Pmode); + rtx target = gen_reg_rtx (Pmode); + + emit_move_insn (index, operands[0]); + emit_insn (gen_subsi3 (index, index, operands[1])); + emit_cmp_and_jump_insns (index, operands[2], GTU, NULL_RTX, SImode, 1, + operands[4]); + + if (Pmode != SImode) + index = convert_to_mode (Pmode, index, 1); + if (GET_CODE (index) != REG) + index = copy_to_mode_reg (Pmode, index); + + if (TARGET_64BIT) + emit_insn (gen_ashldi3 (index, index, GEN_INT (3))); + else + emit_insn (gen_ashlsi3 (index, index, const2_rtx)); + + emit_move_insn (base, gen_rtx_LABEL_REF (Pmode, operands[3])); + + index = gen_const_mem (Pmode, gen_rtx_PLUS (Pmode, base, index)); + emit_move_insn (target, index); + + if (flag_pic) + target = gen_rtx_PLUS (Pmode, base, target); + emit_jump_insn (gen_casesi_jump (target, operands[3])); + + DONE; +}) + + +;; +;;- Jump to subroutine. +;; +;; + +; +; untyped call instruction pattern(s). +; + +;; 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; +}) + +;; 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_BLOCKAGE)] + "" + "" + [(set_attr "type" "none") + (set_attr "length" "0")]) + +; +; sibcall patterns +; + +(define_expand "sibcall" + [(call (match_operand 0 "" "") + (match_operand 1 "" ""))] + "" +{ + s390_emit_call (XEXP (operands[0], 0), NULL_RTX, NULL_RTX, NULL_RTX); + DONE; +}) + +(define_insn "*sibcall_br" + [(call (mem:QI (reg SIBCALL_REGNUM)) + (match_operand 0 "const_int_operand" "n"))] + "SIBLING_CALL_P (insn) + && GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode" + "br\t%%r1" + [(set_attr "op_type" "RR") + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + +(define_insn "*sibcall_brc" + [(call (mem:QI (match_operand 0 "bras_sym_operand" "X")) + (match_operand 1 "const_int_operand" "n"))] + "SIBLING_CALL_P (insn) && TARGET_SMALL_EXEC" + "j\t%0" + [(set_attr "op_type" "RI") + (set_attr "type" "branch")]) + +(define_insn "*sibcall_brcl" + [(call (mem:QI (match_operand 0 "bras_sym_operand" "X")) + (match_operand 1 "const_int_operand" "n"))] + "SIBLING_CALL_P (insn) && TARGET_CPU_ZARCH" + "jg\t%0" + [(set_attr "op_type" "RIL") + (set_attr "type" "branch")]) + +; +; sibcall_value patterns +; + +(define_expand "sibcall_value" + [(set (match_operand 0 "" "") + (call (match_operand 1 "" "") + (match_operand 2 "" "")))] + "" +{ + s390_emit_call (XEXP (operands[1], 0), NULL_RTX, operands[0], NULL_RTX); + DONE; +}) + +(define_insn "*sibcall_value_br" + [(set (match_operand 0 "" "") + (call (mem:QI (reg SIBCALL_REGNUM)) + (match_operand 1 "const_int_operand" "n")))] + "SIBLING_CALL_P (insn) + && GET_MODE (XEXP (XEXP (XEXP (PATTERN (insn), 1), 0), 0)) == Pmode" + "br\t%%r1" + [(set_attr "op_type" "RR") + (set_attr "type" "branch") + (set_attr "atype" "agen")]) + +(define_insn "*sibcall_value_brc" + [(set (match_operand 0 "" "") + (call (mem:QI (match_operand 1 "bras_sym_operand" "X")) + (match_operand 2 "const_int_operand" "n")))] + "SIBLING_CALL_P (insn) && TARGET_SMALL_EXEC" + "j\t%1" + [(set_attr "op_type" "RI") + (set_attr "type" "branch")]) + +(define_insn "*sibcall_value_brcl" + [(set (match_operand 0 "" "") + (call (mem:QI (match_operand 1 "bras_sym_operand" "X")) + (match_operand 2 "const_int_operand" "n")))] + "SIBLING_CALL_P (insn) && TARGET_CPU_ZARCH" + "jg\t%1" + [(set_attr "op_type" "RIL") + (set_attr "type" "branch")]) + + +; +; call instruction pattern(s). +; + +(define_expand "call" + [(call (match_operand 0 "" "") + (match_operand 1 "" "")) + (use (match_operand 2 "" ""))] + "" +{ + s390_emit_call (XEXP (operands[0], 0), NULL_RTX, NULL_RTX, + gen_rtx_REG (Pmode, RETURN_REGNUM)); + DONE; +}) + +(define_insn "*bras" + [(call (mem:QI (match_operand 0 "bras_sym_operand" "X")) + (match_operand 1 "const_int_operand" "n")) + (clobber (match_operand 2 "register_operand" "=r"))] + "!SIBLING_CALL_P (insn) + && TARGET_SMALL_EXEC + && GET_MODE (operands[2]) == Pmode" + "bras\t%2,%0" + [(set_attr "op_type" "RI") + (set_attr "type" "jsr") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "*brasl" + [(call (mem:QI (match_operand 0 "bras_sym_operand" "X")) + (match_operand 1 "const_int_operand" "n")) + (clobber (match_operand 2 "register_operand" "=r"))] + "!SIBLING_CALL_P (insn) + && TARGET_CPU_ZARCH + && GET_MODE (operands[2]) == Pmode" + "brasl\t%2,%0" + [(set_attr "op_type" "RIL") + (set_attr "type" "jsr") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "*basr" + [(call (mem:QI (match_operand 0 "address_operand" "ZQZR")) + (match_operand 1 "const_int_operand" "n")) + (clobber (match_operand 2 "register_operand" "=r"))] + "!SIBLING_CALL_P (insn) && GET_MODE (operands[2]) == Pmode" +{ + if (get_attr_op_type (insn) == OP_TYPE_RR) + return "basr\t%2,%0"; + else + return "bas\t%2,%a0"; +} + [(set (attr "op_type") + (if_then_else (match_operand 0 "register_operand" "") + (const_string "RR") (const_string "RX"))) + (set_attr "type" "jsr") + (set_attr "atype" "agen") + (set_attr "z196prop" "z196_cracked")]) + +; +; call_value instruction pattern(s). +; + +(define_expand "call_value" + [(set (match_operand 0 "" "") + (call (match_operand 1 "" "") + (match_operand 2 "" ""))) + (use (match_operand 3 "" ""))] + "" +{ + s390_emit_call (XEXP (operands[1], 0), NULL_RTX, operands[0], + gen_rtx_REG (Pmode, RETURN_REGNUM)); + DONE; +}) + +(define_insn "*bras_r" + [(set (match_operand 0 "" "") + (call (mem:QI (match_operand 1 "bras_sym_operand" "X")) + (match_operand:SI 2 "const_int_operand" "n"))) + (clobber (match_operand 3 "register_operand" "=r"))] + "!SIBLING_CALL_P (insn) + && TARGET_SMALL_EXEC + && GET_MODE (operands[3]) == Pmode" + "bras\t%3,%1" + [(set_attr "op_type" "RI") + (set_attr "type" "jsr") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "*brasl_r" + [(set (match_operand 0 "" "") + (call (mem:QI (match_operand 1 "bras_sym_operand" "X")) + (match_operand 2 "const_int_operand" "n"))) + (clobber (match_operand 3 "register_operand" "=r"))] + "!SIBLING_CALL_P (insn) + && TARGET_CPU_ZARCH + && GET_MODE (operands[3]) == Pmode" + "brasl\t%3,%1" + [(set_attr "op_type" "RIL") + (set_attr "type" "jsr") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "*basr_r" + [(set (match_operand 0 "" "") + (call (mem:QI (match_operand 1 "address_operand" "ZQZR")) + (match_operand 2 "const_int_operand" "n"))) + (clobber (match_operand 3 "register_operand" "=r"))] + "!SIBLING_CALL_P (insn) && GET_MODE (operands[3]) == Pmode" +{ + if (get_attr_op_type (insn) == OP_TYPE_RR) + return "basr\t%3,%1"; + else + return "bas\t%3,%a1"; +} + [(set (attr "op_type") + (if_then_else (match_operand 1 "register_operand" "") + (const_string "RR") (const_string "RX"))) + (set_attr "type" "jsr") + (set_attr "atype" "agen") + (set_attr "z196prop" "z196_cracked")]) + +;; +;;- Thread-local storage support. +;; + +(define_expand "get_tp_64" + [(set (match_operand:DI 0 "nonimmediate_operand" "") (reg:DI TP_REGNUM))] + "TARGET_64BIT" + "") + +(define_expand "get_tp_31" + [(set (match_operand:SI 0 "nonimmediate_operand" "") (reg:SI TP_REGNUM))] + "!TARGET_64BIT" + "") + +(define_expand "set_tp_64" + [(set (reg:DI TP_REGNUM) (match_operand:DI 0 "nonimmediate_operand" "")) + (set (reg:DI TP_REGNUM) (unspec_volatile:DI [(reg:DI TP_REGNUM)] UNSPECV_SET_TP))] + "TARGET_64BIT" + "") + +(define_expand "set_tp_31" + [(set (reg:SI TP_REGNUM) (match_operand:SI 0 "nonimmediate_operand" "")) + (set (reg:SI TP_REGNUM) (unspec_volatile:SI [(reg:SI TP_REGNUM)] UNSPECV_SET_TP))] + "!TARGET_64BIT" + "") + +(define_insn "*set_tp" + [(set (reg TP_REGNUM) (unspec_volatile [(reg TP_REGNUM)] UNSPECV_SET_TP))] + "" + "" + [(set_attr "type" "none") + (set_attr "length" "0")]) + +(define_insn "*tls_load_64" + [(set (match_operand:DI 0 "register_operand" "=d") + (unspec:DI [(match_operand:DI 1 "memory_operand" "RT") + (match_operand:DI 2 "" "")] + UNSPEC_TLS_LOAD))] + "TARGET_64BIT" + "lg\t%0,%1%J2" + [(set_attr "op_type" "RXE") + (set_attr "z10prop" "z10_fwd_A3")]) + +(define_insn "*tls_load_31" + [(set (match_operand:SI 0 "register_operand" "=d,d") + (unspec:SI [(match_operand:SI 1 "memory_operand" "R,T") + (match_operand:SI 2 "" "")] + UNSPEC_TLS_LOAD))] + "!TARGET_64BIT" + "@ + l\t%0,%1%J2 + ly\t%0,%1%J2" + [(set_attr "op_type" "RX,RXY") + (set_attr "type" "load") + (set_attr "z10prop" "z10_fwd_A3,z10_fwd_A3")]) + +(define_insn "*bras_tls" + [(set (match_operand 0 "" "") + (call (mem:QI (match_operand 1 "bras_sym_operand" "X")) + (match_operand 2 "const_int_operand" "n"))) + (clobber (match_operand 3 "register_operand" "=r")) + (use (match_operand 4 "" ""))] + "!SIBLING_CALL_P (insn) + && TARGET_SMALL_EXEC + && GET_MODE (operands[3]) == Pmode" + "bras\t%3,%1%J4" + [(set_attr "op_type" "RI") + (set_attr "type" "jsr") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "*brasl_tls" + [(set (match_operand 0 "" "") + (call (mem:QI (match_operand 1 "bras_sym_operand" "X")) + (match_operand 2 "const_int_operand" "n"))) + (clobber (match_operand 3 "register_operand" "=r")) + (use (match_operand 4 "" ""))] + "!SIBLING_CALL_P (insn) + && TARGET_CPU_ZARCH + && GET_MODE (operands[3]) == Pmode" + "brasl\t%3,%1%J4" + [(set_attr "op_type" "RIL") + (set_attr "type" "jsr") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "*basr_tls" + [(set (match_operand 0 "" "") + (call (mem:QI (match_operand 1 "address_operand" "ZQZR")) + (match_operand 2 "const_int_operand" "n"))) + (clobber (match_operand 3 "register_operand" "=r")) + (use (match_operand 4 "" ""))] + "!SIBLING_CALL_P (insn) && GET_MODE (operands[3]) == Pmode" +{ + if (get_attr_op_type (insn) == OP_TYPE_RR) + return "basr\t%3,%1%J4"; + else + return "bas\t%3,%a1%J4"; +} + [(set (attr "op_type") + (if_then_else (match_operand 1 "register_operand" "") + (const_string "RR") (const_string "RX"))) + (set_attr "type" "jsr") + (set_attr "atype" "agen") + (set_attr "z196prop" "z196_cracked")]) + +;; +;;- Atomic operations +;; + +; +; memory barrier pattern. +; + +(define_expand "memory_barrier" + [(set (match_dup 0) + (unspec:BLK [(match_dup 0)] UNSPEC_MB))] + "" +{ + operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); + MEM_VOLATILE_P (operands[0]) = 1; +}) + +(define_insn "*memory_barrier" + [(set (match_operand:BLK 0 "" "") + (unspec:BLK [(match_dup 0)] UNSPEC_MB))] + "" + "bcr\t15,0" + [(set_attr "op_type" "RR")]) + +; Although bcr is superscalar on Z10, this variant will never become part of +; an execution group. + +; +; compare and swap patterns. +; + +(define_expand "sync_compare_and_swap<mode>" + [(parallel + [(set (match_operand:TDSI 0 "register_operand" "") + (match_operand:TDSI 1 "memory_operand" "")) + (set (match_dup 1) + (unspec_volatile:TDSI + [(match_dup 1) + (match_operand:TDSI 2 "register_operand" "") + (match_operand:TDSI 3 "register_operand" "")] + UNSPECV_CAS)) + (set (reg:CCZ1 CC_REGNUM) + (compare:CCZ1 (match_dup 1) (match_dup 2)))])] + "") + +(define_expand "sync_compare_and_swap<mode>" + [(parallel + [(set (match_operand:HQI 0 "register_operand" "") + (match_operand:HQI 1 "memory_operand" "")) + (set (match_dup 1) + (unspec_volatile:HQI + [(match_dup 1) + (match_operand:HQI 2 "general_operand" "") + (match_operand:HQI 3 "general_operand" "")] + UNSPECV_CAS)) + (clobber (reg:CC CC_REGNUM))])] + "" + "s390_expand_cs_hqi (<MODE>mode, operands[0], operands[1], + operands[2], operands[3]); DONE;") + +; cds, cdsg +(define_insn "*sync_compare_and_swap<mode>" + [(set (match_operand:DW 0 "register_operand" "=r") + (match_operand:DW 1 "memory_operand" "+Q")) + (set (match_dup 1) + (unspec_volatile:DW + [(match_dup 1) + (match_operand:DW 2 "register_operand" "0") + (match_operand:DW 3 "register_operand" "r")] + UNSPECV_CAS)) + (set (reg:CCZ1 CC_REGNUM) + (compare:CCZ1 (match_dup 1) (match_dup 2)))] + "" + "cds<tg>\t%0,%3,%S1" + [(set_attr "op_type" "RS<TE>") + (set_attr "type" "sem")]) + +; cs, csg +(define_insn "*sync_compare_and_swap<mode>" + [(set (match_operand:GPR 0 "register_operand" "=r") + (match_operand:GPR 1 "memory_operand" "+Q")) + (set (match_dup 1) + (unspec_volatile:GPR + [(match_dup 1) + (match_operand:GPR 2 "register_operand" "0") + (match_operand:GPR 3 "register_operand" "r")] + UNSPECV_CAS)) + (set (reg:CCZ1 CC_REGNUM) + (compare:CCZ1 (match_dup 1) (match_dup 2)))] + "" + "cs<g>\t%0,%3,%S1" + [(set_attr "op_type" "RS<E>") + (set_attr "type" "sem")]) + + +; +; Other atomic instruction patterns. +; + +(define_expand "sync_lock_test_and_set<mode>" + [(match_operand:HQI 0 "register_operand") + (match_operand:HQI 1 "memory_operand") + (match_operand:HQI 2 "general_operand")] + "" + "s390_expand_atomic (<MODE>mode, SET, operands[0], operands[1], + operands[2], false); DONE;") + +; z196 load and add, xor, or and and instructions + +; lan, lang, lao, laog, lax, laxg, laa, laag +(define_insn "sync_<atomic><mode>" + [(parallel + [(set (match_operand:GPR 0 "memory_operand" "+QS") + (unspec_volatile:GPR + [(ATOMIC_Z196:GPR (match_dup 0) + (match_operand:GPR 1 "general_operand" "d"))] + UNSPECV_ATOMIC_OP)) + (clobber (match_scratch:GPR 2 "=d")) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_Z196" + "la<noxa><g>\t%2,%1,%0") + +; lan, lang, lao, laog, lax, laxg, laa, laag +(define_insn "sync_old_<atomic><mode>" + [(parallel + [(set (match_operand:GPR 0 "register_operand" "=d") + (match_operand:GPR 1 "memory_operand" "+QS")) + (set (match_dup 1) + (unspec_volatile:GPR + [(ATOMIC_Z196:GPR (match_dup 1) + (match_operand:GPR 2 "general_operand" "d"))] + UNSPECV_ATOMIC_OP)) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_Z196" + "la<noxa><g>\t%0,%2,%1") + + +(define_expand "sync_<atomic><mode>" + [(set (match_operand:HQI 0 "memory_operand") + (ATOMIC:HQI (match_dup 0) + (match_operand:HQI 1 "general_operand")))] + "" + "s390_expand_atomic (<MODE>mode, <CODE>, NULL_RTX, operands[0], + operands[1], false); DONE;") + +(define_expand "sync_old_<atomic><mode>" + [(set (match_operand:HQI 0 "register_operand") + (match_operand:HQI 1 "memory_operand")) + (set (match_dup 1) + (ATOMIC:HQI (match_dup 1) + (match_operand:HQI 2 "general_operand")))] + "" + "s390_expand_atomic (<MODE>mode, <CODE>, operands[0], operands[1], + operands[2], false); DONE;") + +(define_expand "sync_new_<atomic><mode>" + [(set (match_operand:HQI 0 "register_operand") + (ATOMIC:HQI (match_operand:HQI 1 "memory_operand") + (match_operand:HQI 2 "general_operand"))) + (set (match_dup 1) (ATOMIC:HQI (match_dup 1) (match_dup 2)))] + "" + "s390_expand_atomic (<MODE>mode, <CODE>, operands[0], operands[1], + operands[2], true); DONE;") + +;; +;;- Miscellaneous instructions. +;; + +; +; allocate stack instruction pattern(s). +; + +(define_expand "allocate_stack" + [(match_operand 0 "general_operand" "") + (match_operand 1 "general_operand" "")] + "TARGET_BACKCHAIN" +{ + rtx temp = gen_reg_rtx (Pmode); + + emit_move_insn (temp, s390_back_chain_rtx ()); + anti_adjust_stack (operands[1]); + emit_move_insn (s390_back_chain_rtx (), temp); + + emit_move_insn (operands[0], virtual_stack_dynamic_rtx); + DONE; +}) + + +; +; setjmp instruction pattern. +; + +(define_expand "builtin_setjmp_receiver" + [(match_operand 0 "" "")] + "flag_pic" +{ + emit_insn (s390_load_got ()); + emit_use (pic_offset_table_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 "general_operand" "") + (match_operand 1 "general_operand" "")] + "" + "DONE;") + +(define_expand "restore_stack_function" + [(match_operand 0 "general_operand" "") + (match_operand 1 "general_operand" "")] + "" + "DONE;") + +(define_expand "restore_stack_block" + [(match_operand 0 "register_operand" "") + (match_operand 1 "register_operand" "")] + "TARGET_BACKCHAIN" +{ + rtx temp = gen_reg_rtx (Pmode); + + emit_move_insn (temp, s390_back_chain_rtx ()); + emit_move_insn (operands[0], operands[1]); + emit_move_insn (s390_back_chain_rtx (), temp); + + DONE; +}) + +(define_expand "save_stack_nonlocal" + [(match_operand 0 "memory_operand" "") + (match_operand 1 "register_operand" "")] + "" +{ + rtx base = gen_rtx_REG (Pmode, BASE_REGNUM); + + /* Copy the backchain to the first word, sp to the second and the + literal pool base to the third. */ + + rtx save_bc = adjust_address (operands[0], Pmode, 0); + rtx save_sp = adjust_address (operands[0], Pmode, GET_MODE_SIZE (Pmode)); + rtx save_bp = adjust_address (operands[0], Pmode, 2 * GET_MODE_SIZE (Pmode)); + + if (TARGET_BACKCHAIN) + emit_move_insn (save_bc, force_reg (Pmode, s390_back_chain_rtx ())); + + emit_move_insn (save_sp, operands[1]); + emit_move_insn (save_bp, base); + + DONE; +}) + +(define_expand "restore_stack_nonlocal" + [(match_operand 0 "register_operand" "") + (match_operand 1 "memory_operand" "")] + "" +{ + rtx base = gen_rtx_REG (Pmode, BASE_REGNUM); + rtx temp = NULL_RTX; + + /* Restore the backchain from the first word, sp from the second and the + literal pool base from the third. */ + + rtx save_bc = adjust_address (operands[1], Pmode, 0); + rtx save_sp = adjust_address (operands[1], Pmode, GET_MODE_SIZE (Pmode)); + rtx save_bp = adjust_address (operands[1], Pmode, 2 * GET_MODE_SIZE (Pmode)); + + if (TARGET_BACKCHAIN) + temp = force_reg (Pmode, save_bc); + + emit_move_insn (base, save_bp); + emit_move_insn (operands[0], save_sp); + + if (temp) + emit_move_insn (s390_back_chain_rtx (), temp); + + emit_use (base); + DONE; +}) + +(define_expand "exception_receiver" + [(const_int 0)] + "" +{ + s390_set_has_landing_pad_p (true); + DONE; +}) + +; +; nop instruction pattern(s). +; + +(define_insn "nop" + [(const_int 0)] + "" + "lr\t0,0" + [(set_attr "op_type" "RR") + (set_attr "z10prop" "z10_fr_E1")]) + +(define_insn "nop1" + [(const_int 1)] + "" + "lr\t1,1" + [(set_attr "op_type" "RR")]) + + +; +; Special literal pool access instruction pattern(s). +; + +(define_insn "*pool_entry" + [(unspec_volatile [(match_operand 0 "consttable_operand" "X")] + UNSPECV_POOL_ENTRY)] + "" +{ + enum machine_mode mode = GET_MODE (PATTERN (insn)); + unsigned int align = GET_MODE_BITSIZE (mode); + s390_output_pool_entry (operands[0], mode, align); + return ""; +} + [(set (attr "length") + (symbol_ref "GET_MODE_SIZE (GET_MODE (PATTERN (insn)))"))]) + +(define_insn "pool_align" + [(unspec_volatile [(match_operand 0 "const_int_operand" "n")] + UNSPECV_POOL_ALIGN)] + "" + ".align\t%0" + [(set (attr "length") (symbol_ref "INTVAL (operands[0])"))]) + +(define_insn "pool_section_start" + [(unspec_volatile [(const_int 1)] UNSPECV_POOL_SECTION)] + "" + ".section\t.rodata" + [(set_attr "length" "0")]) + +(define_insn "pool_section_end" + [(unspec_volatile [(const_int 0)] UNSPECV_POOL_SECTION)] + "" + ".previous" + [(set_attr "length" "0")]) + +(define_insn "main_base_31_small" + [(set (match_operand 0 "register_operand" "=a") + (unspec [(label_ref (match_operand 1 "" ""))] UNSPEC_MAIN_BASE))] + "!TARGET_CPU_ZARCH && GET_MODE (operands[0]) == Pmode" + "basr\t%0,0" + [(set_attr "op_type" "RR") + (set_attr "type" "la") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "main_base_31_large" + [(set (match_operand 0 "register_operand" "=a") + (unspec [(label_ref (match_operand 1 "" ""))] UNSPEC_MAIN_BASE)) + (set (pc) (label_ref (match_operand 2 "" "")))] + "!TARGET_CPU_ZARCH && GET_MODE (operands[0]) == Pmode" + "bras\t%0,%2" + [(set_attr "op_type" "RI") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "main_base_64" + [(set (match_operand 0 "register_operand" "=a") + (unspec [(label_ref (match_operand 1 "" ""))] UNSPEC_MAIN_BASE))] + "TARGET_CPU_ZARCH && GET_MODE (operands[0]) == Pmode" + "larl\t%0,%1" + [(set_attr "op_type" "RIL") + (set_attr "type" "larl") + (set_attr "z10prop" "z10_fwd_A1")]) + +(define_insn "main_pool" + [(set (match_operand 0 "register_operand" "=a") + (unspec_volatile [(const_int 0)] UNSPECV_MAIN_POOL))] + "GET_MODE (operands[0]) == Pmode" +{ + gcc_unreachable (); +} + [(set (attr "type") + (if_then_else (ne (symbol_ref "TARGET_CPU_ZARCH") (const_int 0)) + (const_string "larl") (const_string "la")))]) + +(define_insn "reload_base_31" + [(set (match_operand 0 "register_operand" "=a") + (unspec [(label_ref (match_operand 1 "" ""))] UNSPEC_RELOAD_BASE))] + "!TARGET_CPU_ZARCH && GET_MODE (operands[0]) == Pmode" + "basr\t%0,0\;la\t%0,%1-.(%0)" + [(set_attr "length" "6") + (set_attr "type" "la") + (set_attr "z196prop" "z196_cracked")]) + +(define_insn "reload_base_64" + [(set (match_operand 0 "register_operand" "=a") + (unspec [(label_ref (match_operand 1 "" ""))] UNSPEC_RELOAD_BASE))] + "TARGET_CPU_ZARCH && GET_MODE (operands[0]) == Pmode" + "larl\t%0,%1" + [(set_attr "op_type" "RIL") + (set_attr "type" "larl") + (set_attr "z10prop" "z10_fwd_A1")]) + +(define_insn "pool" + [(unspec_volatile [(match_operand 0 "const_int_operand" "n")] UNSPECV_POOL)] + "" +{ + gcc_unreachable (); +} + [(set (attr "length") (symbol_ref "INTVAL (operands[0])"))]) + +;; +;; Insns related to generating the function prologue and epilogue. +;; + + +(define_expand "prologue" + [(use (const_int 0))] + "" + "s390_emit_prologue (); DONE;") + +(define_expand "epilogue" + [(use (const_int 1))] + "" + "s390_emit_epilogue (false); DONE;") + +(define_expand "sibcall_epilogue" + [(use (const_int 0))] + "" + "s390_emit_epilogue (true); DONE;") + +(define_insn "*return" + [(return) + (use (match_operand 0 "register_operand" "a"))] + "GET_MODE (operands[0]) == Pmode" + "br\t%0" + [(set_attr "op_type" "RR") + (set_attr "type" "jsr") + (set_attr "atype" "agen")]) + + +;; Instruction definition to extend a 31-bit pointer into a 64-bit +;; pointer. This is used for compatibility. + +(define_expand "ptr_extend" + [(set (match_operand:DI 0 "register_operand" "=r") + (match_operand:SI 1 "register_operand" "r"))] + "TARGET_64BIT" +{ + emit_insn (gen_anddi3 (operands[0], + gen_lowpart (DImode, operands[1]), + GEN_INT (0x7fffffff))); + DONE; +}) + +;; Instruction definition to expand eh_return macro to support +;; swapping in special linkage return addresses. + +(define_expand "eh_return" + [(use (match_operand 0 "register_operand" ""))] + "TARGET_TPF" +{ + s390_emit_tpf_eh_return (operands[0]); + DONE; +}) + +; +; Stack Protector Patterns +; + +(define_expand "stack_protect_set" + [(set (match_operand 0 "memory_operand" "") + (match_operand 1 "memory_operand" ""))] + "" +{ +#ifdef TARGET_THREAD_SSP_OFFSET + operands[1] + = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), + GEN_INT (TARGET_THREAD_SSP_OFFSET))); +#endif + 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_set<mode>" + [(set (match_operand:DSI 0 "memory_operand" "=Q") + (unspec:DSI [(match_operand:DSI 1 "memory_operand" "Q")] UNSPEC_SP_SET))] + "" + "mvc\t%O0(%G0,%R0),%S1" + [(set_attr "op_type" "SS")]) + +(define_expand "stack_protect_test" + [(set (reg:CC CC_REGNUM) + (compare (match_operand 0 "memory_operand" "") + (match_operand 1 "memory_operand" ""))) + (match_operand 2 "" "")] + "" +{ + rtx cc_reg, test; +#ifdef TARGET_THREAD_SSP_OFFSET + operands[1] + = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), + GEN_INT (TARGET_THREAD_SSP_OFFSET))); +#endif + if (TARGET_64BIT) + emit_insn (gen_stack_protect_testdi (operands[0], operands[1])); + else + emit_insn (gen_stack_protect_testsi (operands[0], operands[1])); + + cc_reg = gen_rtx_REG (CCZmode, CC_REGNUM); + test = gen_rtx_EQ (VOIDmode, cc_reg, const0_rtx); + emit_jump_insn (gen_cbranchcc4 (test, cc_reg, const0_rtx, operands[2])); + DONE; +}) + +(define_insn "stack_protect_test<mode>" + [(set (reg:CCZ CC_REGNUM) + (unspec:CCZ [(match_operand:DSI 0 "memory_operand" "Q") + (match_operand:DSI 1 "memory_operand" "Q")] UNSPEC_SP_TEST))] + "" + "clc\t%O0(%G0,%R0),%S1" + [(set_attr "op_type" "SS")]) + +; This is used in s390_emit_prologue in order to prevent insns +; adjusting the stack pointer to be moved over insns writing stack +; slots using a copy of the stack pointer in a different register. +(define_insn "stack_tie" + [(set (match_operand:BLK 0 "memory_operand" "+m") + (unspec:BLK [(match_dup 0)] UNSPEC_TIE))] + "" + "" + [(set_attr "length" "0")]) + + +; +; Data prefetch patterns +; + +(define_insn "prefetch" + [(prefetch (match_operand 0 "address_operand" "ZQZRZSZT,X") + (match_operand:SI 1 "const_int_operand" " n,n") + (match_operand:SI 2 "const_int_operand" " n,n"))] + "TARGET_Z10" +{ + switch (which_alternative) + { + case 0: + return INTVAL (operands[1]) == 1 ? "pfd\t2,%a0" : "pfd\t1,%a0"; + case 1: + if (larl_operand (operands[0], Pmode)) + return INTVAL (operands[1]) == 1 ? "pfdrl\t2,%a0" : "pfdrl\t1,%a0"; + default: + + /* This might be reached for symbolic operands with an odd + addend. We simply omit the prefetch for such rare cases. */ + + return ""; + } +} + [(set_attr "type" "load,larl") + (set_attr "op_type" "RXY,RIL") + (set_attr "z10prop" "z10_super") + (set_attr "z196prop" "z196_alone")]) + + +; +; Byte swap instructions +; + +(define_insn "bswap<mode>2" + [(set (match_operand:GPR 0 "register_operand" "=d, d") + (bswap:GPR (match_operand:GPR 1 "nonimmediate_operand" " d,RT")))] + "TARGET_CPU_ZARCH" + "@ + lrv<g>r\t%0,%1 + lrv<g>\t%0,%1" + [(set_attr "type" "*,load") + (set_attr "op_type" "RRE,RXY") + (set_attr "z10prop" "z10_super")]) + + +; +; Population count instruction +; + +; The S/390 popcount instruction counts the bits of op1 in 8 byte +; portions and stores the result in the corresponding bytes in op0. +(define_insn "*popcount<mode>" + [(set (match_operand:INT 0 "register_operand" "=d") + (unspec:INT [(match_operand:INT 1 "register_operand" "d")] UNSPEC_POPCNT)) + (clobber (reg:CC CC_REGNUM))] + "TARGET_Z196" + "popcnt\t%0,%1" + [(set_attr "op_type" "RRE")]) + +(define_expand "popcountdi2" + [; popcnt op0, op1 + (parallel [(set (match_operand:DI 0 "register_operand" "") + (unspec:DI [(match_operand:DI 1 "register_operand")] + UNSPEC_POPCNT)) + (clobber (reg:CC CC_REGNUM))]) + ; sllg op2, op0, 32 + (set (match_dup 2) (ashift:DI (match_dup 0) (const_int 32))) + ; agr op0, op2 + (parallel [(set (match_dup 0) (plus:DI (match_dup 0) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))]) + ; sllg op2, op0, 16 + (set (match_dup 2) + (ashift:DI (match_dup 0) (const_int 16))) + ; agr op0, op2 + (parallel [(set (match_dup 0) (plus:DI (match_dup 0) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))]) + ; sllg op2, op0, 8 + (set (match_dup 2) (ashift:DI (match_dup 0) (const_int 8))) + ; agr op0, op2 + (parallel [(set (match_dup 0) (plus:DI (match_dup 0) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))]) + ; srlg op0, op0, 56 + (set (match_dup 0) (lshiftrt:DI (match_dup 0) (const_int 56)))] + "TARGET_Z196 && TARGET_64BIT" + "operands[2] = gen_reg_rtx (DImode);") + +(define_expand "popcountsi2" + [; popcnt op0, op1 + (parallel [(set (match_operand:SI 0 "register_operand" "") + (unspec:SI [(match_operand:SI 1 "register_operand")] + UNSPEC_POPCNT)) + (clobber (reg:CC CC_REGNUM))]) + ; sllk op2, op0, 16 + (set (match_dup 2) + (ashift:SI (match_dup 0) (const_int 16))) + ; ar op0, op2 + (parallel [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))]) + ; sllk op2, op0, 8 + (set (match_dup 2) (ashift:SI (match_dup 0) (const_int 8))) + ; ar op0, op2 + (parallel [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))]) + ; srl op0, op0, 24 + (set (match_dup 0) (lshiftrt:SI (match_dup 0) (const_int 24)))] + "TARGET_Z196" + "operands[2] = gen_reg_rtx (SImode);") + +(define_expand "popcounthi2" + [; popcnt op0, op1 + (parallel [(set (match_operand:HI 0 "register_operand" "") + (unspec:HI [(match_operand:HI 1 "register_operand")] + UNSPEC_POPCNT)) + (clobber (reg:CC CC_REGNUM))]) + ; sllk op2, op0, 8 + (set (match_dup 2) + (ashift:SI (match_dup 0) (const_int 8))) + ; ar op0, op2 + (parallel [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 2))) + (clobber (reg:CC CC_REGNUM))]) + ; srl op0, op0, 8 + (set (match_dup 0) (lshiftrt:HI (match_dup 0) (const_int 8)))] + "TARGET_Z196" + "operands[2] = gen_reg_rtx (SImode);") + +(define_expand "popcountqi2" + [; popcnt op0, op1 + (parallel [(set (match_operand:QI 0 "register_operand" "") + (unspec:QI [(match_operand:QI 1 "register_operand")] + UNSPEC_POPCNT)) + (clobber (reg:CC CC_REGNUM))])] + "TARGET_Z196" + "") + +;; +;;- Copy sign instructions +;; + +(define_insn "copysign<mode>3" + [(set (match_operand:FP 0 "register_operand" "=f") + (unspec:FP [(match_operand:FP 1 "register_operand" "<fT0>") + (match_operand:FP 2 "register_operand" "f")] + UNSPEC_COPYSIGN))] + "TARGET_Z196" + "cpsdr\t%0,%2,%1" + [(set_attr "op_type" "RRF") + (set_attr "type" "fsimp<mode>")]) diff --git a/gcc/config/s390/s390.opt b/gcc/config/s390/s390.opt new file mode 100644 index 000000000..3a3cd42bf --- /dev/null +++ b/gcc/config/s390/s390.opt @@ -0,0 +1,99 @@ +; Options for the S/390 / zSeries port of the compiler. + +; Copyright (C) 2005, 2006, 2007, 2010 Free Software Foundation, Inc. +; +; 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/>. + +m31 +Target Report RejectNegative Negative(m64) InverseMask(64BIT) +31 bit ABI + +m64 +Target Report RejectNegative Negative(m31) Mask(64BIT) +64 bit ABI + +march= +Target RejectNegative Joined Var(s390_arch_string) +Generate code for given CPU + +mbackchain +Target Report Mask(BACKCHAIN) +Maintain backchain pointer + +mdebug +Target Report Mask(DEBUG_ARG) +Additional debug prints + +mesa +Target Report RejectNegative Negative(mzarch) InverseMask(ZARCH) +ESA/390 architecture + +mhard-dfp +Target Report Mask(HARD_DFP) +Enable decimal floating point hardware support + +mhard-float +Target Report RejectNegative Negative(msoft-float) InverseMask(SOFT_FLOAT, HARD_FLOAT) +Enable hardware floating point + +mlong-double-128 +Target Report RejectNegative Negative(mlong-double-64) Mask(LONG_DOUBLE_128) +Use 128-bit long double + +mlong-double-64 +Target Report RejectNegative Negative(mlong-double-128) InverseMask(LONG_DOUBLE_128) +Use 64-bit long double + +mpacked-stack +Target Report Mask(PACKED_STACK) +Use packed stack layout + +msmall-exec +Target Report Mask(SMALL_EXEC) +Use bras for executable < 64k + +msoft-float +Target Report RejectNegative Negative(mhard-float) Mask(SOFT_FLOAT) +Disable hardware floating point + +mstack-guard= +Target RejectNegative Joined +Set the max. number of bytes which has to be left to stack size before a trap instruction is triggered + +mstack-size= +Target RejectNegative Joined +Emit extra code in the function prologue in order to trap if the stack size exceeds the given limit + +mtune= +Target RejectNegative Joined +Schedule code for given CPU + +mmvcle +Target Report Mask(MVCLE) +mvcle use + +mwarn-dynamicstack +Target RejectNegative Var(s390_warn_dynamicstack_p) +Warn if a function uses alloca or creates an array with dynamic size + +mwarn-framesize= +Target RejectNegative Joined +Warn if a single function's framesize exceeds the given framesize + +mzarch +Target Report RejectNegative Negative(mesa) Mask(ZARCH) +z/Architecture diff --git a/gcc/config/s390/s390x.h b/gcc/config/s390/s390x.h new file mode 100644 index 000000000..3712eb156 --- /dev/null +++ b/gcc/config/s390/s390x.h @@ -0,0 +1,27 @@ +/* Definitions of target machine for IBM zSeries 64-bit + Copyright (C) 2002, 2007 Free Software Foundation, Inc. + Contributed by Hartmut Penner (hpenner@de.ibm.com) and + Ulrich Weigand (uweigand@de.ibm.com). + +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/>. */ + +#ifndef _S390X_H +#define _S390X_H + +#define DEFAULT_TARGET_64BIT + +#endif diff --git a/gcc/config/s390/t-linux64 b/gcc/config/s390/t-linux64 new file mode 100644 index 000000000..cc6ab3670 --- /dev/null +++ b/gcc/config/s390/t-linux64 @@ -0,0 +1,11 @@ +# On Debian, Ubuntu and other derivative distributions, the 32bit libraries +# are found in /lib32 and /usr/lib32, /lib64 and /usr/lib64 are symlinks to +# /lib and /usr/lib, while other distributions install libraries into /lib64 +# and /usr/lib64. The LSB does not enforce the use of /lib64 and /usr/lib64, +# it doesn't tell anything about the 32bit libraries on those systems. Set +# MULTILIB_OSDIRNAMES according to what is found on the target. + +MULTILIB_OPTIONS = m64/m31 +MULTILIB_DIRNAMES = 64 32 +MULTILIB_OSDIRNAMES = ../lib64$(call if_multiarch,:s390x-linux-gnu) +MULTILIB_OSDIRNAMES += $(if $(wildcard $(shell echo $(SYSTEM_HEADER_DIR))/../../usr/lib32),../lib32,../lib)$(call if_multiarch,:s390-linux-gnu) diff --git a/gcc/config/s390/tpf-unwind.h b/gcc/config/s390/tpf-unwind.h new file mode 100644 index 000000000..33fd5f5c8 --- /dev/null +++ b/gcc/config/s390/tpf-unwind.h @@ -0,0 +1,252 @@ +/* DWARF2 EH unwinding support for TPF OS. + Copyright (C) 2004, 2005, 2009 Free Software Foundation, Inc. + Contributed by P.J. Darcy (darcypj@us.ibm.com). + +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. + +Under Section 7 of GPL version 3, you are granted additional +permissions described in the GCC Runtime Library Exception, version +3.1, as published by the Free Software Foundation. + +You should have received a copy of the GNU General Public License and +a copy of the GCC Runtime Library Exception along with this program; +see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +<http://www.gnu.org/licenses/>. */ + +#include <dlfcn.h> + +/* Function Name: __isPATrange + Parameters passed into it: address to check + Return Value: A 1 if address is in pat code "range", 0 if not + Description: This function simply checks to see if the address + passed to it is in the CP pat code range. */ + +#define MIN_PATRANGE 0x10000 +#define MAX_PATRANGE 0x800000 + +static inline unsigned int +__isPATrange (void *addr) +{ + if (addr > (void *)MIN_PATRANGE && addr < (void *)MAX_PATRANGE) + return 1; + else + return 0; +} + +/* TPF return address offset from start of stack frame. */ +#define TPFRA_OFFSET 168 + +/* Exceptions macro defined for TPF so that functions without + dwarf frame information can be used with exceptions. */ +#define MD_FALLBACK_FRAME_STATE_FOR s390_fallback_frame_state + +static _Unwind_Reason_Code +s390_fallback_frame_state (struct _Unwind_Context *context, + _Unwind_FrameState *fs) +{ + unsigned long int regs; + unsigned long int new_cfa; + int i; + + regs = *((unsigned long int *) + (((unsigned long int) context->cfa) - STACK_POINTER_OFFSET)); + + /* Are we going through special linkage code? */ + if (__isPATrange (context->ra)) + { + + /* Our return register isn't zero for end of stack, so + check backward stackpointer to see if it is zero. */ + if (regs == NULL) + return _URC_END_OF_STACK; + + /* No stack frame. */ + fs->regs.cfa_how = CFA_REG_OFFSET; + fs->regs.cfa_reg = 15; + fs->regs.cfa_offset = STACK_POINTER_OFFSET; + + /* All registers remain unchanged ... */ + for (i = 0; i < 32; i++) + { + fs->regs.reg[i].how = REG_SAVED_REG; + fs->regs.reg[i].loc.reg = i; + } + + /* ... except for %r14, which is stored at CFA-112 + and used as return address. */ + fs->regs.reg[14].how = REG_SAVED_OFFSET; + fs->regs.reg[14].loc.offset = TPFRA_OFFSET - STACK_POINTER_OFFSET; + fs->retaddr_column = 14; + + return _URC_NO_REASON; + } + + regs = *((unsigned long int *) + (((unsigned long int) context->cfa) - STACK_POINTER_OFFSET)); + new_cfa = regs + STACK_POINTER_OFFSET; + + fs->regs.cfa_how = CFA_REG_OFFSET; + fs->regs.cfa_reg = 15; + fs->regs.cfa_offset = new_cfa - + (unsigned long int) context->cfa + STACK_POINTER_OFFSET; + + for (i = 0; i < 16; i++) + { + fs->regs.reg[i].how = REG_SAVED_OFFSET; + fs->regs.reg[i].loc.offset = regs + i*8 - new_cfa; + } + + for (i = 0; i < 4; i++) + { + fs->regs.reg[16 + i].how = REG_SAVED_OFFSET; + fs->regs.reg[16 + i].loc.offset = regs + 16*8 + i*8 - new_cfa; + } + + fs->retaddr_column = 14; + + return _URC_NO_REASON; +} + +/* Function Name: __tpf_eh_return + Parameters passed into it: Destination address to jump to. + Return Value: Converted Destination address if a Pat Stub exists. + Description: This function swaps the unwinding return address + with the cp stub code. The original target return address is + then stored into the tpf return address field. The cp stub + code is searched for by climbing back up the stack and + comparing the tpf stored return address object address to + that of the targets object address. */ + +#define CURRENT_STACK_PTR() \ + ({ register unsigned long int *stack_ptr asm ("%r15"); stack_ptr; }) + +#define PREVIOUS_STACK_PTR() \ + ((unsigned long int *)(*(CURRENT_STACK_PTR()))) + +#define RA_OFFSET 112 +#define R15_OFFSET 120 +#define TPFAREA_OFFSET 160 +#define TPFAREA_SIZE STACK_POINTER_OFFSET-TPFAREA_OFFSET +#define INVALID_RETURN 0 + +void * __tpf_eh_return (void *target); + +void * +__tpf_eh_return (void *target) +{ + Dl_info targetcodeInfo, currentcodeInfo; + int retval; + void *current, *stackptr, *destination_frame; + unsigned long int shifter, is_a_stub; + + is_a_stub = 0; + + /* Get code info for target return's address. */ + retval = dladdr (target, &targetcodeInfo); + + /* Ensure the code info is valid (for target). */ + if (retval != INVALID_RETURN) + { + + /* Get the stack pointer of the stack frame to be modified by + the exception unwinder. So that we can begin our climb + there. */ + stackptr = (void *) *((unsigned long int *) (*(PREVIOUS_STACK_PTR()))); + + /* Begin looping through stack frames. Stop if invalid + code information is retrieved or if a match between the + current stack frame iteration shared object's address + matches that of the target, calculated above. */ + do + { + /* Get return address based on our stackptr iterator. */ + current = (void *) *((unsigned long int *) + (stackptr+RA_OFFSET)); + + /* Is it a Pat Stub? */ + if (__isPATrange (current)) + { + /* Yes it was, get real return address + in TPF stack area. */ + current = (void *) *((unsigned long int *) + (stackptr+TPFRA_OFFSET)); + is_a_stub = 1; + } + + /* Get codeinfo on RA so that we can figure out + the module address. */ + retval = dladdr (current, ¤tcodeInfo); + + /* Check that codeinfo for current stack frame is valid. + Then compare the module address of current stack frame + to target stack frame to determine if we have the pat + stub address we want. Also ensure we are dealing + with a module crossing, stub return address. */ + if (is_a_stub && retval != INVALID_RETURN + && targetcodeInfo.dli_fbase == currentcodeInfo.dli_fbase) + { + /* Yes! They are in the same module. + Force copy of TPF private stack area to + destination stack frame TPF private area. */ + destination_frame = (void *) *((unsigned long int *) + (*PREVIOUS_STACK_PTR() + R15_OFFSET)); + + /* Copy TPF linkage area from current frame to + destination frame. */ + memcpy((void *) (destination_frame + TPFAREA_OFFSET), + (void *) (stackptr + TPFAREA_OFFSET), TPFAREA_SIZE); + + /* Now overlay the + real target address into the TPF stack area of + the target frame we are jumping to. */ + *((unsigned long int *) (destination_frame + + TPFRA_OFFSET)) = (unsigned long int) target; + + /* Before returning the desired pat stub address to + the exception handling unwinder so that it can + actually do the "leap" shift out the low order + bit designated to determine if we are in 64BIT mode. + This is necessary for CTOA stubs. + Otherwise we leap one byte past where we want to + go to in the TPF pat stub linkage code. */ + shifter = *((unsigned long int *) + (stackptr + RA_OFFSET)); + + shifter &= ~1ul; + + /* Store Pat Stub Address in destination Stack Frame. */ + *((unsigned long int *) (destination_frame + + RA_OFFSET)) = shifter; + + /* Re-adjust pat stub address to go to correct place + in linkage. */ + shifter = shifter - 4; + + return (void *) shifter; + } + + /* Desired module pat stub not found ... + Bump stack frame iterator. */ + stackptr = (void *) *(unsigned long int *) stackptr; + + is_a_stub = 0; + + } while (stackptr && retval != INVALID_RETURN + && targetcodeInfo.dli_fbase != currentcodeInfo.dli_fbase); + } + + /* No pat stub found, could be a problem? Simply return unmodified + target address. */ + return target; +} + diff --git a/gcc/config/s390/tpf.h b/gcc/config/s390/tpf.h new file mode 100644 index 000000000..d2a0f966b --- /dev/null +++ b/gcc/config/s390/tpf.h @@ -0,0 +1,130 @@ +/* Definitions for target OS TPF for GNU compiler, for IBM S/390 hardware + Copyright (C) 2003, 2004, 2005, 2007, 2009, + 2010 Free Software Foundation, Inc. + Contributed by P.J. Darcy (darcypj@us.ibm.com), + Hartmut Penner (hpenner@de.ibm.com), and + Ulrich Weigand (uweigand@de.ibm.com). + +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/>. */ + +#ifndef _TPF_H +#define _TPF_H + +/* TPF wants the following macros defined/undefined as follows. */ +#undef TARGET_TPF +#define TARGET_TPF 1 +#undef ASM_APP_ON +#define ASM_APP_ON "#APP\n" +#undef ASM_APP_OFF +#define ASM_APP_OFF "#NO_APP\n" +#define NO_IMPLICIT_EXTERN_C +#define TARGET_POSIX_IO + +#undef SIZE_TYPE +#define SIZE_TYPE ("long unsigned int") +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE ("long int") +#undef WCHAR_TYPE +#define WCHAR_TYPE "int" +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE 32 + + +/* Basic record keeping for the TPF OS name. */ +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (TPF: zSeries)"); + +/* TPF OS specific stack-pointer offset. */ +#undef STACK_POINTER_OFFSET +#define STACK_POINTER_OFFSET 448 + +/* When building for TPF, set a generic default target that is 64 bits. Also + enable TPF profiling support and the standard backchain by default. */ +#undef TARGET_DEFAULT +#define TARGET_DEFAULT (MASK_TPF_PROFILING | MASK_64BIT | MASK_ZARCH \ + | MASK_HARD_DFP | MASK_BACKCHAIN) + +/* Exception handling. */ + +/* Select a format to encode pointers in exception handling data. */ +#undef ASM_PREFERRED_EH_DATA_FORMAT +#define ASM_PREFERRED_EH_DATA_FORMAT(CODE, GLOBAL) DW_EH_PE_absptr + +/* TPF OS specific compiler settings. */ +#undef TARGET_OS_CPP_BUILTINS +#define TARGET_OS_CPP_BUILTINS() \ + do \ + { \ + builtin_define_std ("tpf"); \ + builtin_assert ("system=tpf"); \ + builtin_define ("__ELF__"); \ + } \ + while (0) + + +#define EXTRA_SPECS \ + { "entry_spec", ENTRY_SPEC } + +/* Make TPF specific spec file settings here. */ + +#undef STARTFILE_SPEC +#define STARTFILE_SPEC \ + "%{mmain:crt0%O%s} crtbeginS%O%s crt3%O%s" + +#undef ENDFILE_SPEC +#define ENDFILE_SPEC "crtendS%O%s" + +#undef CC1_SPEC +#define CC1_SPEC "%{!fverbose-asm: -fverbose-asm}" + +/* The GNU C++ standard library requires that these macros be defined. */ +#undef CPLUSPLUS_CPP_SPEC +#define CPLUSPLUS_CPP_SPEC "-D_GNU_SOURCE %(cpp)" + +#undef ASM_SPEC +#define ASM_SPEC "%{m31&m64}%{mesa&mzarch}%{march=*} \ + -alshd=%b.lst" + +/* It would be nice to get the system linker script define the ones that it + needed. */ +#undef LIB_SPEC +#define LIB_SPEC "-lCTIS -lCISO -lCLBM -lCTAL -lCFVS -lCTBX -lCTXO \ + -lCJ00 -lCTDF -lCOMX -lCOMS -lCTHD -lCTAD -lTPFSTUB" + +#undef TARGET_C99_FUNCTIONS +#define TARGET_C99_FUNCTIONS 1 + +#define ENTRY_SPEC "%{mmain:-entry=_start} \ + %{!mmain:-entry=0}" + +/* All linking is done shared on TPF-OS. */ +/* FIXME: When binutils patch for new emulation is committed + then change emulation to elf64_s390_tpf. */ +#undef LINK_SPEC +#define LINK_SPEC \ + "-m elf64_s390 \ + %{static:%estatic is not supported on TPF-OS} \ + %{shared: -shared} \ + %{!shared:-shared} \ + %(entry_spec)" + +#define MD_UNWIND_SUPPORT "config/s390/tpf-unwind.h" + +/* IBM copies these libraries over with these names. */ +#define MATH_LIBRARY "CLBM" +#define LIBSTDCXX "CPP1" +#endif /* ! _TPF_H */ diff --git a/gcc/config/s390/tpf.md b/gcc/config/s390/tpf.md new file mode 100644 index 000000000..e1106a052 --- /dev/null +++ b/gcc/config/s390/tpf.md @@ -0,0 +1,33 @@ +;; S390 TPF-OS specific machine patterns +;; Copyright (C) 2005, 2007 Free Software Foundation, Inc. +;; +;; 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/>. + +(define_insn "prologue_tpf" + [(unspec_volatile [(const_int 0)] UNSPECV_TPF_PROLOGUE) + (clobber (reg:DI 1))] + "TARGET_TPF_PROFILING" + "larl\t%%r1,.+14\;tm\t4065,255\;bnz\t4064" + [(set_attr "length" "14")]) + + +(define_insn "epilogue_tpf" + [(unspec_volatile [(const_int 0)] UNSPECV_TPF_EPILOGUE) + (clobber (reg:DI 1))] + "TARGET_TPF_PROFILING" + "larl\t%%r1,.+14\;tm\t4071,255\;bnz\t4070" + [(set_attr "length" "14")]) diff --git a/gcc/config/s390/tpf.opt b/gcc/config/s390/tpf.opt new file mode 100644 index 000000000..c3cde22b6 --- /dev/null +++ b/gcc/config/s390/tpf.opt @@ -0,0 +1,27 @@ +; Options for the TPF-OS port of the compiler. + +; Copyright (C) 2005, 2007 Free Software Foundation, Inc. +; +; 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/>. + +mtpf-trace +Target Report Mask(TPF_PROFILING) +Enable TPF-OS tracing code + +mmain +Target Report +Specify main object for TPF-OS |