xref: /netbsd-src/external/gpl3/gcc/dist/gcc/config/rs6000/genfusion.pl (revision 0a3071956a3a9fdebdbf7f338cf2d439b45fc728)
1#!/usr/bin/perl
2# Generate fusion.md
3#
4# Copyright (C) 2020-2022 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-2022 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_one
57{
58  my ($lmode, $result, $ccmode) = @_;
59
60  my $np = "NON_PREFIXED_D";
61  my $mempred = "non_update_memory_operand";
62  my $extend;
63
64  # We need to special case lwa.  The prefixed_load_p function in rs6000.cc
65  # (which determines if a load instruction is prefixed) uses the fact that the
66  # register mode is different from the memory mode, and that the sign_extend
67  # attribute is set to use DS-form rules for the address instead of D-form.
68  # If the register size is the same, prefixed_load_p assumes we are doing a
69  # lwz.  We change to use an lwz and word compare if we don't need to sign
70  # extend the SImode value.  Otherwise if we need the value, we need to
71  # make sure the insn is marked as ds-form.
72  my $cmp_size_char = ($lmode eq "SI"
73		       && $ccmode eq "CC"
74		       && $result !~ /^EXT|^DI$/) ? "w" : "d";
75
76  if ($ccmode eq "CC") {
77    # ld and lwa are both DS-FORM.
78    ($lmode eq "DI") and $np = "NON_PREFIXED_DS";
79    ($lmode eq "SI" && $cmp_size_char eq "d") and $np = "NON_PREFIXED_DS";
80  } else {
81    if ($lmode eq "DI") {
82      # ld is DS-form, but lwz is not.
83      $np = "NON_PREFIXED_DS";
84    }
85  }
86
87  my $cmpl = ($ccmode eq "CC") ? "" : "l";
88  my $echr = ($ccmode eq "CC" && $cmp_size_char eq "d") ? "a" : "z";
89  if ($lmode eq "DI") { $echr = ""; }
90  my $constpred = ($ccmode eq "CC") ? "const_m1_to_1_operand"
91  				    : "const_0_to_1_operand";
92
93  # For clobber, we need a SI/DI reg in case we
94  # split because we have to sign/zero extend.
95  my $clobbermode = ($lmode =~ /^[QH]I$/) ? "GPR" : $lmode;
96  if ($result =~ /^EXT/ || $result eq "GPR" || $clobbermode eq "GPR") {
97    # We always need extension if result > lmode.
98    $extend = ($ccmode eq "CC") ? "sign" : "zero";
99  } else {
100    # Result of SI/DI does not need sign extension.
101    $extend = "none";
102  }
103
104  my $ldst = mode_to_ldst_char($lmode);
105
106  # DS-form addresses need YZ, and not m.
107  my $constraint = ($np eq "NON_PREFIXED_DS") ? "YZ" : "m";
108  print <<HERE;
109;; load-cmpi fusion pattern generated by gen_ld_cmpi_p10
110;; load mode is $lmode result mode is $result compare mode is $ccmode extend is $extend
111(define_insn_and_split "*l${ldst}${echr}_cmp${cmpl}${cmp_size_char}i_cr0_${lmode}_${result}_${ccmode}_${extend}"
112  [(set (match_operand:${ccmode} 2 "cc_reg_operand" "=x")
113        (compare:${ccmode} (match_operand:${lmode} 1 "${mempred}" "${constraint}")
114HERE
115  print "   " if $ccmode eq "CCUNS";
116print <<HERE;
117                    (match_operand:${lmode} 3 "${constpred}" "n")))
118HERE
119
120  if ($result eq "clobber") {
121    print <<HERE;
122   (clobber (match_scratch:${clobbermode} 0 "=r"))]
123HERE
124  } elsif ($result eq $lmode) {
125    print <<HERE;
126   (set (match_operand:${result} 0 "gpc_reg_operand" "=r") (match_dup 1))]
127HERE
128  } else {
129    print <<HERE;
130   (set (match_operand:${result} 0 "gpc_reg_operand" "=r") (${extend}_extend:${result} (match_dup 1)))]
131HERE
132  }
133
134  print <<HERE;
135  "(TARGET_P10_FUSION)"
136  "l${ldst}${echr}%X1 %0,%1\\;cmp${cmpl}${cmp_size_char}i %2,%0,%3"
137  "&& reload_completed
138   && (cc_reg_not_cr0_operand (operands[2], CCmode)
139       || !address_is_non_pfx_d_or_x (XEXP (operands[1], 0),
140                                      ${lmode}mode, ${np}))"
141HERE
142
143  if ($extend eq "none") {
144    print "  [(set (match_dup 0) (match_dup 1))\n";
145  } elsif ($result eq "clobber") {
146    print "  [(set (match_dup 0) (${extend}_extend:${clobbermode} (match_dup 1)))\n";
147  } else {
148    print "  [(set (match_dup 0) (${extend}_extend:${result} (match_dup 1)))\n";
149  }
150
151  print <<HERE;
152   (set (match_dup 2)
153        (compare:${ccmode} (match_dup 0) (match_dup 3)))]
154  ""
155  [(set_attr "type" "fused_load_cmpi")
156   (set_attr "cost" "8")
157HERE
158
159  if ($lmode eq "SI" && $ccmode eq "CC" && $cmp_size_char eq "d") {
160    # prefixed_load_p needs the sign_extend attribute to validate lwa as a
161    # DS-form instruction instead of D-form.
162    print "   (set_attr \"sign_extend\" \"yes\")\n";
163  }
164
165  print <<HERE
166   (set_attr "length" "8")])
167
168HERE
169}
170
171sub gen_ld_cmpi_p10
172{
173  foreach my $lmode (qw/DI SI HI QI/) {
174    foreach my $result ("clobber", $lmode,  "EXT$lmode") {
175      # EXTDI does not exist, and we cannot directly produce HI/QI results.
176      next if $result =~ /^(QI|HI|EXTDI)$/;
177
178      # Don't allow EXTQI because that would allow HI result which we can't do.
179      $result = "GPR" if $result eq "EXTQI";
180
181      foreach my $ccmode (qw/CC CCUNS/) {
182	# We do not have signed single-byte loads.
183	next if ($lmode eq "QI" and $ccmode eq "CC");
184
185	gen_ld_cmpi_p10_one($lmode, $result, $ccmode);
186      }
187    }
188  }
189}
190
191sub gen_logical_addsubf
192{
193    my @logicals = ( "and", "andc", "eqv", "nand", "nor", "or", "orc", "xor" );
194    my %logicals_addsub = ( "and"=>1, "nand"=>1, "nor"=>1, "or"=>1 );
195    my @addsub = ( "add", "subf" );
196    my %isaddsub = ( "add"=>1, "subf"=>1 );
197    my %complement = ( "and"=> 0, "andc"=> 1, "eqv"=> 0, "nand"=> 3,
198		       "nor"=> 3, "or"=> 0, "orc"=> 1, "xor"=> 0,
199		       "add"=> 0, "subf"=> 0 );
200    my %invert = ( "and"=> 0, "andc"=> 0, "eqv"=> 1, "nand"=> 0,
201		   "nor"=> 0, "or"=> 0, "orc"=> 0, "xor"=> 0,
202		   "add"=> 0, "subf"=> 0 );
203    my %commute2 = ( "and"=> 1, "andc"=> 0, "eqv"=> 1, "nand"=> 0,
204		     "nor"=> 0, "or"=> 1, "orc"=> 0, "xor"=> 1 );
205    my %rtlop = ( "and"=>"and", "andc"=>"and", "eqv"=>"xor", "nand"=>"ior",
206		  "nor"=>"and", "or"=>"ior", "orc"=>"ior", "xor"=>"xor",
207		  "add"=>"plus", "subf"=>"minus" );
208
209    my ($kind, $vchr, $mode, $pred, $constraint, $cr, $outer, @outer_ops,
210	$outer_op, $outer_comp, $outer_inv, $outer_rtl, $inner, @inner_ops,
211	$inner_comp, $inner_inv, $inner_rtl, $inner_op, $both_commute, $c4,
212	$bc, $inner_arg0, $inner_arg1, $inner_exp, $outer_arg2, $outer_exp,
213	$ftype, $insn, $is_subf, $is_rsubf, $outer_32, $outer_42,$outer_name,
214	$fuse_type);
215  KIND: foreach $kind ('scalar','vector') {
216      @outer_ops = @logicals;
217      if ( $kind eq 'vector' ) {
218	  $vchr = "v";
219	  $mode = "VM";
220	  $pred = "altivec_register_operand";
221	  $constraint = "v";
222	  $fuse_type = "fused_vector";
223      } else {
224	  $vchr = "";
225	  $mode = "GPR";
226	  $pred = "gpc_reg_operand";
227	  $constraint = "r";
228	  $fuse_type = "fused_arith_logical";
229	  push (@outer_ops, @addsub);
230	  push (@outer_ops, ( "rsubf" ));
231      }
232      $c4 = "${constraint},${constraint},${constraint},${constraint}";
233    OUTER: foreach $outer ( @outer_ops ) {
234	$outer_name = "${vchr}${outer}";
235	$is_subf = ( $outer eq "subf" );
236	$is_rsubf = ( $outer eq "rsubf" );
237	if ( $is_rsubf ) {
238	    $outer = "subf";
239	}
240	$outer_op = "${vchr}${outer}";
241	$outer_comp = $complement{$outer};
242	$outer_inv = $invert{$outer};
243	$outer_rtl = $rtlop{$outer};
244	@inner_ops = @logicals;
245	$ftype = "logical-logical";
246	if ( exists $isaddsub{$outer} ) {
247	    @inner_ops = sort keys %logicals_addsub;
248	    $ftype = "logical-add";
249	} elsif ( $kind ne 'vector' && exists $logicals_addsub{$outer} ) {
250	    push (@inner_ops, @addsub);
251	}
252      INNER: foreach $inner ( @inner_ops ) {
253	  if ( exists $isaddsub{$inner} ) {
254	      $ftype = "add-logical";
255	  }
256	  $inner_comp = $complement{$inner};
257	  $inner_inv = $invert{$inner};
258	  $inner_rtl = $rtlop{$inner};
259	  $inner_op = "${vchr}${inner}";
260	  # If both ops commute then we can specify % on operand 1
261	  # so the pattern will let operands 1 and 2 interchange.
262	  $both_commute = ($inner eq $outer) && ($commute2{$inner} == 1);
263	  $bc = ""; if ( $both_commute ) { $bc = "%"; }
264	  $inner_arg0 = "(match_operand:${mode} 0 \"${pred}\" \"${c4}\")";
265	  $inner_arg1 = "(match_operand:${mode} 1 \"${pred}\" \"${bc}${c4}\")";
266	  if ( ($inner_comp & 1) == 1 ) {
267	      $inner_arg0 = "(not:${mode} $inner_arg0)";
268	  }
269	  if ( ($inner_comp & 2) == 2 ) {
270	      $inner_arg1 = "(not:${mode} $inner_arg1)";
271	  }
272	  $inner_exp = "(${inner_rtl}:${mode} ${inner_arg0}
273                          ${inner_arg1})";
274	  if ( $inner_inv == 1 ) {
275	      $inner_exp = "(not:${mode} $inner_exp)";
276	  }
277	  $outer_arg2 = "(match_operand:${mode} 2 \"${pred}\" \"${c4}\")";
278	  if ( ($outer_comp & 1) == 1 ) {
279	      $outer_arg2 = "(not:${mode} $outer_arg2)";
280	  }
281	  if ( ($outer_comp & 2) == 2 ) {
282	      $inner_exp = "(not:${mode} $inner_exp)";
283	  }
284	  if ( $is_subf ) {
285	      $outer_32 = "%2,%3";
286	      $outer_42 = "%2,%4";
287	  } else {
288	      $outer_32 = "%3,%2";
289	      $outer_42 = "%4,%2";
290	  }
291	  if ( $is_rsubf == 1 ) {
292	      $outer_exp = "(${outer_rtl}:${mode} ${outer_arg2}
293                 ${inner_exp})";
294	  } else {
295	      $outer_exp = "(${outer_rtl}:${mode} ${inner_exp}
296                 ${outer_arg2})";
297	  }
298	  if ( $outer_inv == 1 ) {
299	      $outer_exp = "(not:${mode} $outer_exp)";
300	  }
301
302	  $insn =  <<"EOF";
303
304;; $ftype fusion pattern generated by gen_logical_addsubf
305;; $kind $inner_op -> $outer_name
306(define_insn "*fuse_${inner_op}_${outer_name}"
307  [(set (match_operand:${mode} 3 "${pred}" "=&0,&1,&${constraint},${constraint}")
308        ${outer_exp})
309   (clobber (match_scratch:${mode} 4 "=X,X,X,&${constraint}"))]
310  "(TARGET_P10_FUSION)"
311  "@
312   ${inner_op} %3,%1,%0\\;${outer_op} %3,${outer_32}
313   ${inner_op} %3,%1,%0\\;${outer_op} %3,${outer_32}
314   ${inner_op} %3,%1,%0\\;${outer_op} %3,${outer_32}
315   ${inner_op} %4,%1,%0\\;${outer_op} %3,${outer_42}"
316  [(set_attr "type" "$fuse_type")
317   (set_attr "cost" "6")
318   (set_attr "length" "8")])
319EOF
320
321	  print $insn;
322      }
323    }
324  }
325}
326
327sub gen_addadd
328{
329    my ($kind, $vchr, $op, $type, $mode, $pred, $constraint);
330    foreach $kind ('scalar','vector') {
331      if ( $kind eq 'vector' ) {
332	  $vchr = "v";
333	  $op = "vaddudm";
334	  $type = "fused_vector";
335	  $mode = "V2DI";
336	  $pred = "altivec_register_operand";
337	  $constraint = "v";
338      } else {
339	  $vchr = "";
340	  $op = "add";
341	  $type = "fused_arith_logical";
342	  $mode = "GPR";
343	  $pred = "gpc_reg_operand";
344	  $constraint = "r";
345      }
346    my $c4 = "${constraint},${constraint},${constraint},${constraint}";
347    print <<"EOF";
348
349;; ${op}-${op} fusion pattern generated by gen_addadd
350(define_insn "*fuse_${op}_${op}"
351  [(set (match_operand:${mode} 3 "${pred}" "=&0,&1,&${constraint},${constraint}")
352        (plus:${mode}
353           (plus:${mode} (match_operand:${mode} 0 "${pred}" "${c4}")
354                     (match_operand:${mode} 1 "${pred}" "%${c4}"))
355           (match_operand:${mode} 2 "${pred}" "${c4}")))
356   (clobber (match_scratch:${mode} 4 "=X,X,X,&${constraint}"))]
357  "(TARGET_P10_FUSION)"
358  "@
359   ${op} %3,%1,%0\\;${op} %3,%3,%2
360   ${op} %3,%1,%0\\;${op} %3,%3,%2
361   ${op} %3,%1,%0\\;${op} %3,%3,%2
362   ${op} %4,%1,%0\\;${op} %3,%4,%2"
363  [(set_attr "type" "${type}")
364   (set_attr "cost" "6")
365   (set_attr "length" "8")])
366EOF
367  }
368}
369
370gen_ld_cmpi_p10();
371gen_logical_addsubf();
372gen_addadd;
373
374exit(0);
375
376