1#!/usr/bin/perl
2# Generate fusion.md
3#
4# Copyright (C) 2020,2021 Free Software Foundation, Inc.
5#
6# This file is part of GCC.
7#
8# GCC is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 3, or (at your option)
11# any later version.
12#
13# GCC is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with GCC; see the file COPYING3.  If not see
20# <http://www.gnu.org/licenses/>.
21
22use warnings;
23use strict;
24
25print <<'EOF';
26;; Generated automatically by genfusion.pl
27
28;; Copyright (C) 2020,2021 Free Software Foundation, Inc.
29;;
30;; This file is part of GCC.
31;;
32;; GCC is free software; you can redistribute it and/or modify it under
33;; the terms of the GNU General Public License as published by the Free
34;; Software Foundation; either version 3, or (at your option) any later
35;; version.
36;;
37;; GCC is distributed in the hope that it will be useful, but WITHOUT ANY
38;; WARRANTY; without even the implied warranty of MERCHANTABILITY or
39;; FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
40;; for more details.
41;;
42;; You should have received a copy of the GNU General Public License
43;; along with GCC; see the file COPYING3.  If not see
44;; <http://www.gnu.org/licenses/>.
45
46EOF
47
48sub mode_to_ldst_char
49{
50    my ($mode) = @_;
51    my %x = (DI => 'd', SI => 'w', HI => 'h', QI => 'b');
52    return $x{$mode} if exists $x{$mode};
53    return '?';
54}
55
56sub gen_ld_cmpi_p10
57{
58    my ($lmode, $ldst, $clobbermode, $result, $cmpl, $echr, $constpred,
59	$mempred, $ccmode, $np, $extend, $resultmode);
60  LMODE: foreach $lmode ('DI','SI','HI','QI') {
61      $ldst = mode_to_ldst_char($lmode);
62      $clobbermode = $lmode;
63      # For clobber, we need a SI/DI reg in case we
64      # split because we have to sign/zero extend.
65      if ($lmode eq 'HI' || $lmode eq 'QI') { $clobbermode = "GPR"; }
66    RESULT: foreach $result ('clobber', $lmode,  "EXT".$lmode) {
67	# EXTDI does not exist, and we cannot directly produce HI/QI results.
68	next RESULT if $result eq "EXTDI" || $result eq "HI" || $result eq "QI";
69	# Don't allow EXTQI because that would allow HI result which we can't do.
70	$result = "GPR" if $result eq "EXTQI";
71      CCMODE: foreach $ccmode ('CC','CCUNS') {
72	  $np = "NON_PREFIXED_D";
73	  $mempred = "non_update_memory_operand";
74	  if ( $ccmode eq 'CC' ) {
75	      next CCMODE if $lmode eq 'QI';
76	      if ( $lmode eq 'DI' || $lmode eq 'SI' ) {
77		  # ld and lwa are both DS-FORM.
78		  $np = "NON_PREFIXED_DS";
79		  $mempred = "ds_form_mem_operand";
80	      }
81	      $cmpl = "";
82	      $echr = "a";
83	      $constpred = "const_m1_to_1_operand";
84	  } else {
85	      if ( $lmode eq 'DI' ) {
86		  # ld is DS-form, but lwz is not.
87		  $np = "NON_PREFIXED_DS";
88		  $mempred = "ds_form_mem_operand";
89	      }
90	      $cmpl = "l";
91	      $echr = "z";
92	      $constpred = "const_0_to_1_operand";
93	  }
94	  if ($lmode eq 'DI') { $echr = ""; }
95	  if ($result =~ m/^EXT/ || $result eq 'GPR' || $clobbermode eq 'GPR') {
96	      # We always need extension if result > lmode.
97	      if ( $ccmode eq 'CC' ) {
98		  $extend = "sign";
99	      } else {
100		  $extend = "zero";
101	      }
102	  } else {
103	      # Result of SI/DI does not need sign extension.
104	      $extend = "none";
105	  }
106	  print ";; load-cmpi fusion pattern generated by gen_ld_cmpi_p10\n";
107	  print ";; load mode is $lmode result mode is $result compare mode is $ccmode extend is $extend\n";
108
109	  print "(define_insn_and_split \"*l${ldst}${echr}_cmp${cmpl}di_cr0_${lmode}_${result}_${ccmode}_${extend}\"\n";
110	  print "  [(set (match_operand:${ccmode} 2 \"cc_reg_operand\" \"=x\")\n";
111	  print "        (compare:${ccmode} (match_operand:${lmode} 1 \"${mempred}\" \"m\")\n";
112	  if ($ccmode eq 'CCUNS') { print "   "; }
113	  print "                    (match_operand:${lmode} 3 \"${constpred}\" \"n\")))\n";
114	  if ($result eq 'clobber') {
115	      print "   (clobber (match_scratch:${clobbermode} 0 \"=r\"))]\n";
116	  } elsif ($result eq $lmode) {
117	      print "   (set (match_operand:${result} 0 \"gpc_reg_operand\" \"=r\") (match_dup 1))]\n";
118	  } else {
119	      print "   (set (match_operand:${result} 0 \"gpc_reg_operand\" \"=r\") (${extend}_extend:${result} (match_dup 1)))]\n";
120	  }
121	  print "  \"(TARGET_P10_FUSION && TARGET_P10_FUSION_LD_CMPI)\"\n";
122	  print "  \"l${ldst}${echr}%X1 %0,%1\\;cmp${cmpl}di %2,%0,%3\"\n";
123	  print "  \"&& reload_completed\n";
124	  print "   && (cc_reg_not_cr0_operand (operands[2], CCmode)\n";
125	  print "       || !address_is_non_pfx_d_or_x (XEXP (operands[1], 0),\n";
126	  print "                                      ${lmode}mode, ${np}))\"\n";
127
128	  if ($extend eq "none") {
129	      print "  [(set (match_dup 0) (match_dup 1))\n";
130	  } else {
131	      $resultmode = $result;
132	      if ( $result eq 'clobber' ) { $resultmode = $clobbermode }
133	      print "  [(set (match_dup 0) (${extend}_extend:${resultmode} (match_dup 1)))\n";
134	  }
135	  print "   (set (match_dup 2)\n";
136	  print "        (compare:${ccmode} (match_dup 0) (match_dup 3)))]\n";
137	  print "  \"\"\n";
138	  print "  [(set_attr \"type\" \"fused_load_cmpi\")\n";
139	  print "   (set_attr \"cost\" \"8\")\n";
140	  print "   (set_attr \"length\" \"8\")])\n";
141	  print "\n";
142      }
143    }
144  }
145}
146
147sub gen_logical_addsubf
148{
149    my @logicals = ( "and", "andc", "eqv", "nand", "nor", "or", "orc", "xor" );
150    my %logicals_addsub = ( "and"=>1, "nand"=>1, "nor"=>1, "or"=>1 );
151    my @addsub = ( "add", "subf" );
152    my %isaddsub = ( "add"=>1, "subf"=>1 );
153    my %complement = ( "and"=> 0, "andc"=> 1, "eqv"=> 0, "nand"=> 3,
154		       "nor"=> 3, "or"=> 0, "orc"=> 1, "xor"=> 0,
155		       "add"=> 0, "subf"=> 0 );
156    my %invert = ( "and"=> 0, "andc"=> 0, "eqv"=> 1, "nand"=> 0,
157		   "nor"=> 0, "or"=> 0, "orc"=> 0, "xor"=> 0,
158		   "add"=> 0, "subf"=> 0 );
159    my %commute2 = ( "and"=> 1, "andc"=> 0, "eqv"=> 1, "nand"=> 0,
160		     "nor"=> 0, "or"=> 1, "orc"=> 0, "xor"=> 1 );
161    my %rtlop = ( "and"=>"and", "andc"=>"and", "eqv"=>"xor", "nand"=>"ior",
162		  "nor"=>"and", "or"=>"ior", "orc"=>"ior", "xor"=>"xor",
163		  "add"=>"plus", "subf"=>"minus" );
164
165    my ($kind, $vchr, $mode, $pred, $constraint, $cr, $outer, @outer_ops,
166	$outer_op, $outer_comp, $outer_inv, $outer_rtl, $inner, @inner_ops,
167	$inner_comp, $inner_inv, $inner_rtl, $inner_op, $both_commute, $c4,
168	$bc, $inner_arg0, $inner_arg1, $inner_exp, $outer_arg2, $outer_exp,
169	$target_flag, $ftype, $insn, $is_subf, $is_rsubf, $outer_32, $outer_42,
170	$outer_name, $fuse_type);
171  KIND: foreach $kind ('scalar','vector') {
172      @outer_ops = @logicals;
173      if ( $kind eq 'vector' ) {
174	  $vchr = "v";
175	  $mode = "VM";
176	  $pred = "altivec_register_operand";
177	  $constraint = "v";
178	  $fuse_type = "fused_vector";
179      } else {
180	  $vchr = "";
181	  $mode = "GPR";
182	  $pred = "gpc_reg_operand";
183	  $constraint = "r";
184	  $fuse_type = "fused_arith_logical";
185	  push (@outer_ops, @addsub);
186	  push (@outer_ops, ( "rsubf" ));
187      }
188      $c4 = "${constraint},${constraint},${constraint},${constraint}";
189    OUTER: foreach $outer ( @outer_ops ) {
190	$outer_name = "${vchr}${outer}";
191	$is_subf = ( $outer eq "subf" );
192	$is_rsubf = ( $outer eq "rsubf" );
193	if ( $is_rsubf ) {
194	    $outer = "subf";
195	}
196	$outer_op = "${vchr}${outer}";
197	$outer_comp = $complement{$outer};
198	$outer_inv = $invert{$outer};
199	$outer_rtl = $rtlop{$outer};
200	@inner_ops = @logicals;
201	$ftype = "logical-logical";
202	$target_flag = "TARGET_P10_FUSION_2LOGICAL";
203	if ( exists $isaddsub{$outer} ) {
204	    @inner_ops = sort keys %logicals_addsub;
205	    $ftype = "logical-add";
206	    $target_flag = "TARGET_P10_FUSION_LOGADD";
207	} elsif ( $kind ne 'vector' && exists $logicals_addsub{$outer} ) {
208	    push (@inner_ops, @addsub);
209	}
210      INNER: foreach $inner ( @inner_ops ) {
211	  if ( exists $isaddsub{$inner} ) {
212	      $ftype = "add-logical";
213	      $target_flag = "TARGET_P10_FUSION_ADDLOG";
214	  }
215	  $inner_comp = $complement{$inner};
216	  $inner_inv = $invert{$inner};
217	  $inner_rtl = $rtlop{$inner};
218	  $inner_op = "${vchr}${inner}";
219	  # If both ops commute then we can specify % on operand 1
220	  # so the pattern will let operands 1 and 2 interchange.
221	  $both_commute = ($inner eq $outer) && ($commute2{$inner} == 1);
222	  $bc = ""; if ( $both_commute ) { $bc = "%"; }
223	  $inner_arg0 = "(match_operand:${mode} 0 \"${pred}\" \"${c4}\")";
224	  $inner_arg1 = "(match_operand:${mode} 1 \"${pred}\" \"${bc}${c4}\")";
225	  if ( ($inner_comp & 1) == 1 ) {
226	      $inner_arg0 = "(not:${mode} $inner_arg0)";
227	  }
228	  if ( ($inner_comp & 2) == 2 ) {
229	      $inner_arg1 = "(not:${mode} $inner_arg1)";
230	  }
231	  $inner_exp = "(${inner_rtl}:${mode} ${inner_arg0}
232                          ${inner_arg1})";
233	  if ( $inner_inv == 1 ) {
234	      $inner_exp = "(not:${mode} $inner_exp)";
235	  }
236	  $outer_arg2 = "(match_operand:${mode} 2 \"${pred}\" \"${c4}\")";
237	  if ( ($outer_comp & 1) == 1 ) {
238	      $outer_arg2 = "(not:${mode} $outer_arg2)";
239	  }
240	  if ( ($outer_comp & 2) == 2 ) {
241	      $inner_exp = "(not:${mode} $inner_exp)";
242	  }
243	  if ( $is_subf ) {
244	      $outer_32 = "%2,%3";
245	      $outer_42 = "%2,%4";
246	  } else {
247	      $outer_32 = "%3,%2";
248	      $outer_42 = "%4,%2";
249	  }
250	  if ( $is_rsubf == 1 ) {
251	      $outer_exp = "(${outer_rtl}:${mode} ${outer_arg2}
252                 ${inner_exp})";
253	  } else {
254	      $outer_exp = "(${outer_rtl}:${mode} ${inner_exp}
255                 ${outer_arg2})";
256	  }
257	  if ( $outer_inv == 1 ) {
258	      $outer_exp = "(not:${mode} $outer_exp)";
259	  }
260
261	  $insn =  <<"EOF";
262
263;; $ftype fusion pattern generated by gen_logical_addsubf
264;; $kind $inner_op -> $outer_name
265(define_insn "*fuse_${inner_op}_${outer_name}"
266  [(set (match_operand:${mode} 3 "${pred}" "=&0,&1,&${constraint},${constraint}")
267        ${outer_exp})
268   (clobber (match_scratch:${mode} 4 "=X,X,X,&r"))]
269  "(TARGET_P10_FUSION && $target_flag)"
270  "@
271   ${inner_op} %3,%1,%0\\;${outer_op} %3,${outer_32}
272   ${inner_op} %3,%1,%0\\;${outer_op} %3,${outer_32}
273   ${inner_op} %3,%1,%0\\;${outer_op} %3,${outer_32}
274   ${inner_op} %4,%1,%0\\;${outer_op} %3,${outer_42}"
275  [(set_attr "type" "$fuse_type")
276   (set_attr "cost" "6")
277   (set_attr "length" "8")])
278EOF
279
280	  print $insn;
281      }
282    }
283  }
284}
285
286sub gen_addadd
287{
288    my ($kind, $vchr, $op, $type, $mode, $pred, $constraint);
289    foreach $kind ('scalar','vector') {
290      if ( $kind eq 'vector' ) {
291	  $vchr = "v";
292	  $op = "vaddudm";
293	  $type = "fused_vector";
294	  $mode = "V2DI";
295	  $pred = "altivec_register_operand";
296	  $constraint = "v";
297      } else {
298	  $vchr = "";
299	  $op = "add";
300	  $type = "fused_arith_logical";
301	  $mode = "GPR";
302	  $pred = "gpc_reg_operand";
303	  $constraint = "r";
304      }
305    my $c4 = "${constraint},${constraint},${constraint},${constraint}";
306    print <<"EOF";
307
308;; ${op}-${op} fusion pattern generated by gen_addadd
309(define_insn "*fuse_${op}_${op}"
310  [(set (match_operand:${mode} 3 "${pred}" "=&0,&1,&${constraint},${constraint}")
311        (plus:${mode}
312           (plus:${mode} (match_operand:${mode} 0 "${pred}" "${c4}")
313                     (match_operand:${mode} 1 "${pred}" "%${c4}"))
314           (match_operand:${mode} 2 "${pred}" "${c4}")))
315   (clobber (match_scratch:${mode} 4 "=X,X,X,&${constraint}"))]
316  "(TARGET_P10_FUSION && TARGET_P10_FUSION_2ADD)"
317  "@
318   ${op} %3,%1,%0\\;${op} %3,%3,%2
319   ${op} %3,%1,%0\\;${op} %3,%3,%2
320   ${op} %3,%1,%0\\;${op} %3,%3,%2
321   ${op} %4,%1,%0\\;${op} %3,%4,%2"
322  [(set_attr "type" "${type}")
323   (set_attr "cost" "6")
324   (set_attr "length" "8")])
325EOF
326  }
327}
328
329gen_ld_cmpi_p10();
330gen_logical_addsubf();
331gen_addadd;
332
333exit(0);
334
335