[1/2] rs6000: Refactor script genfusion.pl

Message ID 55027326-ffe1-87e8-9e4b-08535425afdd@linux.ibm.com
State New
Headers
Series [1/2] rs6000: Refactor script genfusion.pl |

Commit Message

Kewen.Lin Jan. 18, 2023, 8:50 a.m. UTC
  Hi,

As Segher suggested in [1], this patch is to refactor the
script genfusion.pl for generating fusion.md.

It mainly consists of:
  1) Add main subroutine, which calls several backbone
     subroutines, hope it can show the skeleton clearly.
  2) Encapsulate copyright and top comments emission to a
     separated subroutine gen_copyright_and_top_comments.
  3) Remove multiple nested loops in gen_ld_cmpi_p10 by
     expanding them directly, hope it can be more clear.
     Also factor out some logics to ld_cmpi_p10_emit_define
     which aims to focus on define_insn_and_split emission.
     Refine subroutine mode_to_ldst_char a bit.
  4) For gen_logical_addsubf, separate scalar and vector
     handlings into gen_logical_addsubf_{vector,scalar},
     factor out op information querying on complement/invert/
     commute2/"rtl op name" to subroutine
     logical_addsub_get_op_info, factor out some logics on
     define_insn_and_split emission to subroutine
     logical_addsubf_emit_define, and factor out some logic
     to construct inner and outer expression to subroutine
     logical_addsubf_make_exp.
  5) For gen_addadd, it's quite simple so I leave it alone,
     just removes one useless variable.

Note that this patch keeps the fusion.md is exactly the same
as before.

Any comments are highly appreciated.

[1] https://gcc.gnu.org/pipermail/gcc-patches/2022-December/608830.html

BR,
Kewen
-----
gcc/ChangeLog:

	* config/rs6000/genfusion.pl (gen_copyright_and_top_comments): New subroutine,
	refactor from some existing code.
	(mode_to_ldst_char): Adjust with die.
	(ld_cmpi_p10_emit_define): New subroutine, refactor from gen_ld_cmpi_p10,
	emit define_insn_and_split for load-cmpi fusion.
	(gen_ld_cmpi_p10): Adjust with ld_cmpi_p10_emit_define.
	(logical_addsubf_emit_define): New subroutine, refactor from
	gen_logical_addsubf, emit define_insn_and_split for logical/addsubf fusion.
	(logical_addsub_get_op_info): New subroutine, refactor from
	gen_logical_addsubf, offer some information for the given operator.
	(logical_addsubf_make_exp): New subroutine, refactor from
	gen_logical_addsubf, construct the expression used for emission.
	(gen_logical_addsubf_scalar): New subroutine, refactor from
	gen_logical_addsubf, focus on scalar kind of logical/addsubf fusion.
	(gen_logical_addsubf_vector): New subroutine, refactor from
	gen_logical_addsubf, focus on vector kind of logical/addsubf fusion.
	(gen_logical_addsubf): Adjust with calling gen_logical_addsubf_scalar
	and gen_logical_addsubf_vector.
	(gen_addadd): Remove useless variable.
	(main): New subroutine, call the corresponding main subroutine for each
	fusion type.
---
 gcc/config/rs6000/genfusion.pl | 554 ++++++++++++++++++++-------------
 1 file changed, 337 insertions(+), 217 deletions(-)

--
2.37.0
  

Patch

diff --git a/gcc/config/rs6000/genfusion.pl b/gcc/config/rs6000/genfusion.pl
index e4db352e0ce..487e662ce05 100755
--- a/gcc/config/rs6000/genfusion.pl
+++ b/gcc/config/rs6000/genfusion.pl
@@ -22,7 +22,9 @@ 
 use warnings;
 use strict;

-print <<'EOF';
+sub gen_copyright_and_top_comments
+{
+  print << "EOF";
 ;; Generated automatically by genfusion.pl

 ;; Copyright (C) 2020-2023 Free Software Foundation, Inc.
@@ -44,255 +46,369 @@  print <<'EOF';
 ;; <http://www.gnu.org/licenses/>.

 EOF
+}

+# Map any mode of DI/SI/HI/QI to single char d/w/h/b,
+# die if the given mode in arg 0 isn't expected.
 sub mode_to_ldst_char
 {
-    my ($mode) = @_;
-    my %x = (DI => 'd', SI => 'w', HI => 'h', QI => 'b');
-    return $x{$mode} if exists $x{$mode};
-    return '?';
+  my $mode = $_[0];
+  die "Unexpected mode: $mode" unless $mode =~ /[QHSD]I/;
+  my %map = (DI => 'd', SI => 'w', HI => 'h', QI => 'b');
+  return $map{$mode};
 }

-sub gen_ld_cmpi_p10
+# Emit define_insn_and_split for load-cmpi fusion type based
+# on the below given arguments:
+#   arg 0: mode of load.
+#   arg 1: mode of result.
+#   arg 2: mode of comparison.
+#   arg 3: extension type.
+sub ld_cmpi_p10_emit_define
 {
-    my ($lmode, $ldst, $clobbermode, $result, $cmpl, $echr, $constpred,
-	$mempred, $ccmode, $np, $extend, $resultmode);
-  LMODE: foreach $lmode ('DI','SI','HI','QI') {
-      $ldst = mode_to_ldst_char($lmode);
-      $clobbermode = $lmode;
-      # For clobber, we need a SI/DI reg in case we
-      # split because we have to sign/zero extend.
-      if ($lmode eq 'HI' || $lmode eq 'QI') { $clobbermode = "GPR"; }
-    RESULT: foreach $result ('clobber', $lmode,  "EXT".$lmode) {
-	# EXTDI does not exist, and we cannot directly produce HI/QI results.
-	next RESULT if $result eq "EXTDI" || $result eq "HI" || $result eq "QI";
-	# Don't allow EXTQI because that would allow HI result which we can't do.
-	$result = "GPR" if $result eq "EXTQI";
-      CCMODE: foreach $ccmode ('CC','CCUNS') {
-	  $np = "NON_PREFIXED_D";
-	  $mempred = "non_update_memory_operand";
-	  if ( $ccmode eq 'CC' ) {
-	      next CCMODE if $lmode eq 'QI';
-	      if ( $lmode eq 'DI' || $lmode eq 'SI' ) {
-		  # ld and lwa are both DS-FORM.
-		  $np = "NON_PREFIXED_DS";
-		  $mempred = "ds_form_mem_operand";
-	      }
-	      $cmpl = "";
-	      $echr = "a";
-	      $constpred = "const_m1_to_1_operand";
-	  } else {
-	      if ( $lmode eq 'DI' ) {
-		  # ld is DS-form, but lwz is not.
-		  $np = "NON_PREFIXED_DS";
-		  $mempred = "ds_form_mem_operand";
-	      }
-	      $cmpl = "l";
-	      $echr = "z";
-	      $constpred = "const_0_to_1_operand";
-	  }
-	  if ($lmode eq 'DI') { $echr = ""; }
-	  if ($result =~ m/^EXT/ || $result eq 'GPR' || $clobbermode eq 'GPR') {
-	      # We always need extension if result > lmode.
-	      if ( $ccmode eq 'CC' ) {
-		  $extend = "sign";
-	      } else {
-		  $extend = "zero";
-	      }
-	  } else {
-	      # Result of SI/DI does not need sign extension.
-	      $extend = "none";
-	  }
-	  print ";; load-cmpi fusion pattern generated by gen_ld_cmpi_p10\n";
-	  print ";; load mode is $lmode result mode is $result compare mode is $ccmode extend is $extend\n";
-
-	  print "(define_insn_and_split \"*l${ldst}${echr}_cmp${cmpl}di_cr0_${lmode}_${result}_${ccmode}_${extend}\"\n";
-	  print "  [(set (match_operand:${ccmode} 2 \"cc_reg_operand\" \"=x\")\n";
-	  print "        (compare:${ccmode} (match_operand:${lmode} 1 \"${mempred}\" \"m\")\n";
-	  if ($ccmode eq 'CCUNS') { print "   "; }
-	  print "                    (match_operand:${lmode} 3 \"${constpred}\" \"n\")))\n";
-	  if ($result eq 'clobber') {
-	      print "   (clobber (match_scratch:${clobbermode} 0 \"=r\"))]\n";
-	  } elsif ($result eq $lmode) {
-	      print "   (set (match_operand:${result} 0 \"gpc_reg_operand\" \"=r\") (match_dup 1))]\n";
-	  } else {
-	      print "   (set (match_operand:${result} 0 \"gpc_reg_operand\" \"=r\") (${extend}_extend:${result} (match_dup 1)))]\n";
-	  }
-	  print "  \"(TARGET_P10_FUSION)\"\n";
-	  print "  \"l${ldst}${echr}%X1 %0,%1\\;cmp${cmpl}di %2,%0,%3\"\n";
-	  print "  \"&& reload_completed\n";
-	  print "   && (cc_reg_not_cr0_operand (operands[2], CCmode)\n";
-	  print "       || !address_is_non_pfx_d_or_x (XEXP (operands[1], 0),\n";
-	  print "                                      ${lmode}mode, ${np}))\"\n";
-
-	  if ($extend eq "none") {
-	      print "  [(set (match_dup 0) (match_dup 1))\n";
-	  } else {
-	      $resultmode = $result;
-	      if ( $result eq 'clobber' ) { $resultmode = $clobbermode }
-	      print "  [(set (match_dup 0) (${extend}_extend:${resultmode} (match_dup 1)))\n";
-	  }
-	  print "   (set (match_dup 2)\n";
-	  print "        (compare:${ccmode} (match_dup 0) (match_dup 3)))]\n";
-	  print "  \"\"\n";
-	  print "  [(set_attr \"type\" \"fused_load_cmpi\")\n";
-	  print "   (set_attr \"cost\" \"8\")\n";
-	  print "   (set_attr \"length\" \"8\")])\n";
-	  print "\n";
-      }
-    }
+  my $lmode = $_[0];
+  my $result = $_[1];
+  my $ccmode = $_[2];
+  my $extend = $_[3];
+
+  # For clobber, we need a SI/DI reg in case we
+  # split because we have to sign/zero extend.
+  my $clobbermode = ($lmode eq 'HI' || $lmode eq 'QI') ? 'GPR' : $lmode;
+
+  my $np = 'NON_PREFIXED_D';
+  my $mempred = 'non_update_memory_operand';
+
+  # ld and lwa are both DS-FORM.
+  if ( $lmode eq 'DI'
+      || ($lmode eq 'SI' && $ccmode eq 'CC') ) {
+    $np = 'NON_PREFIXED_DS';
+    $mempred = 'ds_form_mem_operand';
+  }
+
+  my $cmpl = '';
+  my $echr = 'a';
+  my $constpred = 'const_m1_to_1_operand';
+
+  # Logical comparison has 'l', zero extended load has 'z'.
+  if ($ccmode eq 'CCUNS') {
+    $cmpl = 'l';
+    $echr = 'z';
+    $constpred = 'const_0_to_1_operand';
   }
+
+  # DI load doesn't have suffix for zero or sign.
+  $echr = '' if $lmode eq 'DI';
+
+  my $ldst = mode_to_ldst_char ($lmode);
+
+  # define_insn_and_split comments.
+  print ";; load-cmpi fusion pattern generated by gen_ld_cmpi_p10\n";
+  print ";; load mode is $lmode result mode is $result compare mode is $ccmode extend is $extend\n";
+
+  # main set pattern.
+  print "(define_insn_and_split \"*l${ldst}${echr}_cmp${cmpl}di_cr0_${lmode}_${result}_${ccmode}_${extend}\"\n";
+  print "  [(set (match_operand:${ccmode} 2 \"cc_reg_operand\" \"=x\")\n";
+  print "        (compare:${ccmode} (match_operand:${lmode} 1 \"${mempred}\" \"m\")\n";
+  print "   " if ($ccmode eq 'CCUNS');
+  print "                    (match_operand:${lmode} 3 \"${constpred}\" \"n\")))\n";
+
+  # clobber or another set pattern.
+  if ($result eq 'clobber') {
+    print "   (clobber (match_scratch:${clobbermode} 0 \"=r\"))]\n";
+  } elsif ($result eq $lmode) {
+    print "   (set (match_operand:${result} 0 \"gpc_reg_operand\" \"=r\") (match_dup 1))]\n";
+  } else {
+    print "   (set (match_operand:${result} 0 \"gpc_reg_operand\" \"=r\") (${extend}_extend:${result} (match_dup 1)))]\n";
+  }
+
+  # insn condition and output template.
+  print "  \"(TARGET_P10_FUSION)\"\n";
+  print "  \"l${ldst}${echr}%X1 %0,%1\\;cmp${cmpl}di %2,%0,%3\"\n";
+
+  # split condition.
+  print "  \"&& reload_completed\n";
+  print "   && (cc_reg_not_cr0_operand (operands[2], CCmode)\n";
+  print "       || !address_is_non_pfx_d_or_x (XEXP (operands[1], 0),\n";
+  print "                                      ${lmode}mode, ${np}))\"\n";
+
+  # new insn patterns.
+  if ($extend eq 'none') {
+    print "  [(set (match_dup 0) (match_dup 1))\n";
+  } else {
+    my $resultmode = ( $result eq 'clobber' ) ? $clobbermode : $result;
+    print "  [(set (match_dup 0) (${extend}_extend:${resultmode} (match_dup 1)))\n";
+  }
+  print "   (set (match_dup 2)\n";
+  print "        (compare:${ccmode} (match_dup 0) (match_dup 3)))]\n";
+  print "  \"\"\n";
+
+  # insn attributes.
+  print "  [(set_attr \"type\" \"fused_load_cmpi\")\n";
+  print "   (set_attr \"cost\" \"8\")\n";
+  print "   (set_attr \"length\" \"8\")])\n";
+  print "\n";
 }

-sub gen_logical_addsubf
+# Main subroutine to generate load-cmpi fusion type.
+sub gen_ld_cmpi_p10
 {
-    my @logicals = ( "and", "andc", "eqv", "nand", "nor", "or", "orc", "xor" );
-    my %logicals_addsub = ( "and"=>1, "nand"=>1, "nor"=>1, "or"=>1 );
-    my @addsub = ( "add", "subf" );
-    my %isaddsub = ( "add"=>1, "subf"=>1 );
-    my %complement = ( "and"=> 0, "andc"=> 1, "eqv"=> 0, "nand"=> 3,
-		       "nor"=> 3, "or"=> 0, "orc"=> 1, "xor"=> 0,
-		       "add"=> 0, "subf"=> 0 );
-    my %invert = ( "and"=> 0, "andc"=> 0, "eqv"=> 1, "nand"=> 0,
-		   "nor"=> 0, "or"=> 0, "orc"=> 0, "xor"=> 0,
-		   "add"=> 0, "subf"=> 0 );
-    my %commute2 = ( "and"=> 1, "andc"=> 0, "eqv"=> 1, "nand"=> 0,
-		     "nor"=> 0, "or"=> 1, "orc"=> 0, "xor"=> 1 );
-    my %rtlop = ( "and"=>"and", "andc"=>"and", "eqv"=>"xor", "nand"=>"ior",
-		  "nor"=>"and", "or"=>"ior", "orc"=>"ior", "xor"=>"xor",
-		  "add"=>"plus", "subf"=>"minus" );
-
-    my ($kind, $vchr, $mode, $pred, $constraint, $cr, $outer, @outer_ops,
-	$outer_op, $outer_comp, $outer_inv, $outer_rtl, $inner, @inner_ops,
-	$inner_comp, $inner_inv, $inner_rtl, $inner_op, $both_commute, $c4,
-	$bc, $inner_arg0, $inner_arg1, $inner_exp, $outer_arg2, $outer_exp,
-	$ftype, $insn, $is_subf, $is_rsubf, $outer_32, $outer_42,$outer_name,
-	$fuse_type);
-  KIND: foreach $kind ('scalar','vector') {
-      @outer_ops = @logicals;
-      if ( $kind eq 'vector' ) {
-	  $vchr = "v";
-	  $mode = "VM";
-	  $pred = "altivec_register_operand";
-	  $constraint = "v";
-	  $fuse_type = "fused_vector";
-      } else {
-	  $vchr = "";
-	  $mode = "GPR";
-	  $pred = "gpc_reg_operand";
-	  $constraint = "r";
-	  $fuse_type = "fused_arith_logical";
-	  push (@outer_ops, @addsub);
-	  push (@outer_ops, ( "rsubf" ));
-      }
-      $c4 = "${constraint},${constraint},${constraint},${constraint}";
-    OUTER: foreach $outer ( @outer_ops ) {
-	$outer_name = "${vchr}${outer}";
-	$is_subf = ( $outer eq "subf" );
-	$is_rsubf = ( $outer eq "rsubf" );
-	if ( $is_rsubf ) {
-	    $outer = "subf";
-	}
-	$outer_op = "${vchr}${outer}";
-	$outer_comp = $complement{$outer};
-	$outer_inv = $invert{$outer};
-	$outer_rtl = $rtlop{$outer};
-	@inner_ops = @logicals;
-	$ftype = "logical-logical";
-	if ( exists $isaddsub{$outer} ) {
-	    @inner_ops = sort keys %logicals_addsub;
-	    $ftype = "logical-add";
-	} elsif ( $kind ne 'vector' && exists $logicals_addsub{$outer} ) {
-	    push (@inner_ops, @addsub);
-	}
-      INNER: foreach $inner ( @inner_ops ) {
-	  if ( exists $isaddsub{$inner} ) {
-	      $ftype = "add-logical";
-	  }
-	  $inner_comp = $complement{$inner};
-	  $inner_inv = $invert{$inner};
-	  $inner_rtl = $rtlop{$inner};
-	  $inner_op = "${vchr}${inner}";
-	  # If both ops commute then we can specify % on operand 1
-	  # so the pattern will let operands 1 and 2 interchange.
-	  $both_commute = ($inner eq $outer) && ($commute2{$inner} == 1);
-	  $bc = ""; if ( $both_commute ) { $bc = "%"; }
-	  $inner_arg0 = "(match_operand:${mode} 0 \"${pred}\" \"${c4}\")";
-	  $inner_arg1 = "(match_operand:${mode} 1 \"${pred}\" \"${bc}${c4}\")";
-	  if ( ($inner_comp & 1) == 1 ) {
-	      $inner_arg0 = "(not:${mode} $inner_arg0)";
-	  }
-	  if ( ($inner_comp & 2) == 2 ) {
-	      $inner_arg1 = "(not:${mode} $inner_arg1)";
-	  }
-	  $inner_exp = "(${inner_rtl}:${mode} ${inner_arg0}
-                          ${inner_arg1})";
-	  if ( $inner_inv == 1 ) {
-	      $inner_exp = "(not:${mode} $inner_exp)";
-	  }
-	  $outer_arg2 = "(match_operand:${mode} 2 \"${pred}\" \"${c4}\")";
-	  if ( ($outer_comp & 1) == 1 ) {
-	      $outer_arg2 = "(not:${mode} $outer_arg2)";
-	  }
-	  if ( ($outer_comp & 2) == 2 ) {
-	      $inner_exp = "(not:${mode} $inner_exp)";
-	  }
-	  if ( $is_subf ) {
-	      $outer_32 = "%2,%3";
-	      $outer_42 = "%2,%4";
-	  } else {
-	      $outer_32 = "%3,%2";
-	      $outer_42 = "%4,%2";
-	  }
-	  if ( $is_rsubf == 1 ) {
-	      $outer_exp = "(${outer_rtl}:${mode} ${outer_arg2}
-                 ${inner_exp})";
-	  } else {
-	      $outer_exp = "(${outer_rtl}:${mode} ${inner_exp}
-                 ${outer_arg2})";
-	  }
-	  if ( $outer_inv == 1 ) {
-	      $outer_exp = "(not:${mode} $outer_exp)";
-	  }
+  # For load mode is DI, there is no EXTDI, the result of
+  # DI doesn't need extension.
+  ld_cmpi_p10_emit_define ('DI', 'clobber', 'CC', 'none');
+  ld_cmpi_p10_emit_define ('DI', 'clobber', 'CCUNS', 'none');
+  ld_cmpi_p10_emit_define ('DI', 'DI', 'CC', 'none');
+  ld_cmpi_p10_emit_define ('DI', 'DI', 'CCUNS', 'none');
+
+  # For load mode is SI, only EXTSI need extension.
+  ld_cmpi_p10_emit_define ('SI', 'clobber', 'CC', 'none');
+  ld_cmpi_p10_emit_define ('SI', 'clobber', 'CCUNS', 'none');
+  ld_cmpi_p10_emit_define ('SI', 'SI', 'CC', 'none');
+  ld_cmpi_p10_emit_define ('SI', 'SI', 'CCUNS', 'none');
+  ld_cmpi_p10_emit_define ('SI', 'EXTSI', 'CC', 'sign');
+  ld_cmpi_p10_emit_define ('SI', 'EXTSI', 'CCUNS', 'zero');

-	  $insn =  <<"EOF";
+  # For load mode is HI, we can't produce HI result directly.
+  # We always need extension if result is wider than load
+  # mode.
+  ld_cmpi_p10_emit_define ('HI', 'clobber', 'CC', 'sign');
+  ld_cmpi_p10_emit_define ('HI', 'clobber', 'CCUNS', 'zero');
+  ld_cmpi_p10_emit_define ('HI', 'EXTHI', 'CC', 'sign');
+  ld_cmpi_p10_emit_define ('HI', 'EXTHI', 'CCUNS', 'zero');
+
+  # For load mode is QI, we can't produce QI result directly,
+  # also ignore CC here.  We always need extension if result
+  # is wider than load mode.
+  ld_cmpi_p10_emit_define ('QI', 'clobber', 'CCUNS', 'zero');
+  # Don't allow EXTQI because that would allow HI result
+  # which we can't do.
+  ld_cmpi_p10_emit_define ('QI', 'GPR', 'CCUNS', 'zero');
+}
+
+# Emit define_insn_and_split for logical/addsubf fusion type
+# based the below given arguments:
+#   arg 0: string for fusion type in comments, can only be
+#          "logical-logical", "logical-add" or "add-logical".
+#   arg 1: "scalar" or "vector".
+#   arg 2: outer operator.
+#   arg 3: inner operator.
+#   arg 4: machine mode.
+#   arg 5: predicate.
+#   arg 6: constraint.
+#   arg 7: expression pattern made by logical_addsubf_make_exp.
+#   arg 8: fusion type for insn type attribute.
+sub logical_addsubf_emit_define
+{
+  my ($ftype, $kind, $outer_op, $inner_op, $mode, $pred, $cstr,
+      $exp, $fuse_type) = @_;
+
+  # Make some adjustments for subf and rsubf.
+  my $outer_name = $outer_op;
+  $outer_op = 'subf' if $outer_op eq 'rsubf';
+
+  my $ops32 = '%3,%2';
+  my $ops42 = '%4,%2';
+  if ( $outer_name eq 'subf' ) {
+    $ops32 = '%2,%3';
+    $ops42 = '%2,%4';
+  }
+
+  print << "EOF";

 ;; $ftype fusion pattern generated by gen_logical_addsubf
 ;; $kind $inner_op -> $outer_name
 (define_insn "*fuse_${inner_op}_${outer_name}"
-  [(set (match_operand:${mode} 3 "${pred}" "=&0,&1,&${constraint},${constraint}")
-        ${outer_exp})
-   (clobber (match_scratch:${mode} 4 "=X,X,X,&${constraint}"))]
+  [(set (match_operand:${mode} 3 "${pred}" "=&0,&1,&${cstr},${cstr}")
+        ${exp})
+   (clobber (match_scratch:${mode} 4 "=X,X,X,&${cstr}"))]
   "(TARGET_P10_FUSION)"
   "@
-   ${inner_op} %3,%1,%0\\;${outer_op} %3,${outer_32}
-   ${inner_op} %3,%1,%0\\;${outer_op} %3,${outer_32}
-   ${inner_op} %3,%1,%0\\;${outer_op} %3,${outer_32}
-   ${inner_op} %4,%1,%0\\;${outer_op} %3,${outer_42}"
+   ${inner_op} %3,%1,%0\\;${outer_op} %3,${ops32}
+   ${inner_op} %3,%1,%0\\;${outer_op} %3,${ops32}
+   ${inner_op} %3,%1,%0\\;${outer_op} %3,${ops32}
+   ${inner_op} %4,%1,%0\\;${outer_op} %3,${ops42}"
   [(set_attr "type" "$fuse_type")
    (set_attr "cost" "6")
    (set_attr "length" "8")])
 EOF
+}

-	  print $insn;
+# For the given operator in arg 0, return an array holding the
+# information on complementing, inverting, commuting and rtl
+# operator name, die if the given operator isn't expected.
+sub logical_addsub_get_op_info
+{
+  my $op = $_[0];
+
+  # Checking the given OP is valid
+  my @valid_ops = ( 'and', 'andc', 'eqv', 'nand', 'nor', 'or', 'orc', 'xor',
+                    'add', 'subf', 'rsubf' );
+  die "Unexpected op:$op" unless grep (/^$op$/, @valid_ops);
+
+  my %complement = ( 'and'=> 0, 'andc'=> 1, 'eqv'=> 0, 'nand'=> 3,
+                     'nor'=> 3, 'or'=> 0, 'orc'=> 1, 'xor'=> 0,
+                     'add'=> 0, 'subf'=> 0 );
+  my %invert     = ( 'and'=> 0, 'andc'=> 0, 'eqv'=> 1, 'nand'=> 0,
+                     'nor'=> 0, 'or'=> 0, 'orc'=> 0, 'xor'=> 0,
+                     'add'=> 0, 'subf'=> 0 );
+  my %commute2   = ( 'and'=> 1, 'andc'=> 0, 'eqv'=> 1, 'nand'=> 0,
+                     'nor'=> 0, 'or'=> 1, 'orc'=> 0, 'xor'=> 1 );
+  my %rtl_ops    = ( 'and'=>'and', 'andc'=>'and', 'eqv'=>'xor', 'nand'=>'ior',
+                     'nor'=>'and', 'or'=>'ior', 'orc'=>'ior', 'xor'=>'xor',
+                     'add'=>'plus', 'subf'=>'minus' );
+
+  return ($complement{$op}, $invert{$op}, $commute2{$op}, $rtl_ops{$op});
+}
+
+# For logical/addsubf fusion type, make up an expression based on
+# the below given arguments:
+#   arg 1: outer operator.
+#   arg 2: inner operator.
+#   arg 3: machine mode.
+#   arg 4: predicate.
+#   arg 5: constraint.
+sub logical_addsubf_make_exp
+{
+  my ($outer_op, $inner_op, $mode, $pred, $cstr) = @_;
+
+  my $outer_is_rsubf_p = 0;
+  if ($outer_op eq 'rsubf') {
+    $outer_op = 'subf';
+    $outer_is_rsubf_p = 1;
+  }
+
+  my ($outer_comp, $outer_invert_p, $outer_comm_p,
+      $outer_rtl_op) = logical_addsub_get_op_info ($outer_op);
+  my ($inner_comp, $inner_invert_p, $inner_comm_p,
+      $inner_rtl_op) = logical_addsub_get_op_info ($inner_op);
+
+  # If both ops commute then we can specify % on operand 1
+  # so the pattern will let operands 1 and 2 interchange.
+  my $bc = '';
+  $bc = '%' if ($inner_op eq $outer_op) && $inner_comm_p;
+
+  my $cstr4 = "${cstr},${cstr},${cstr},${cstr}";
+
+  # Make up arg0 (inner).
+  my $arg0 = "(match_operand:${mode} 0 \"${pred}\" \"${cstr4}\")";
+  $arg0 = "(not:${mode} $arg0)" if ($inner_comp & 1) == 1;
+
+  # Make up arg1 (inner).
+  my $arg1 = "(match_operand:${mode} 1 \"${pred}\" \"${bc}${cstr4}\")";
+  $arg1 = "(not:${mode} $arg1)" if ($inner_comp & 2) == 2;
+
+  # Make up inner_exp.
+  my $inner_exp = "(${inner_rtl_op}:${mode} ${arg0}
+                          ${arg1})";
+
+  # Invert inner_exp if needed.
+  $inner_exp = "(not:${mode} $inner_exp)" if ( $inner_invert_p );
+
+  # Make up outer arg2.
+  my $arg2 = "(match_operand:${mode} 2 \"${pred}\" \"${cstr4}\")";
+  $arg2 = "(not:${mode} $arg2)" if ($outer_comp & 1) == 1;
+
+  # Complement inner_exp if needed.
+  $inner_exp = "(not:${mode} $inner_exp)" if ($outer_comp & 2) == 2;
+
+  my $outer_exp;
+  # Make up outer_exp and special casing rsubf.
+  if ($outer_is_rsubf_p) {
+    $outer_exp = "(${outer_rtl_op}:${mode} ${arg2}
+                 ${inner_exp})";
+  } else {
+    $outer_exp = "(${outer_rtl_op}:${mode} ${inner_exp}
+                 ${arg2})";
+  }
+
+  # Invert outer_exp if needed.
+  $outer_exp = "(not:${mode} $outer_exp)" if $outer_invert_p;
+
+  return $outer_exp;
+}
+
+# Generate logical/addsubf fusion type for scalar.
+sub gen_logical_addsubf_scalar
+{
+  my $mode = 'GPR';
+  my $pred = 'gpc_reg_operand';
+  my $fuse_type = 'fused_arith_logical';
+  my $constraint = 'r';
+
+  my @logicals = ( 'and', 'andc', 'eqv', 'nand', 'nor', 'or', 'orc', 'xor' );
+  # logical ops which can fuse with add/subf/rsubf
+  my @logicals2 = ( 'and', 'nand', 'nor', 'or' );
+  my @add_subf = ( 'add', 'subf' );
+
+  # {logical, add}-logical
+  foreach my $outer_op ( @logicals ) {
+    foreach my $inner_op ( @logicals, @add_subf ) {
+      my $ftype = 'logical-logical';
+      if (grep (/^$inner_op$/, @add_subf)) {
+         next unless grep (/^$outer_op$/, @logicals2);
+         $ftype = 'add-logical';
       }
+      my $exp = logical_addsubf_make_exp ($outer_op, $inner_op, $mode,
+                                          $pred, $constraint);
+      logical_addsubf_emit_define ($ftype, 'scalar', $outer_op, $inner_op,
+                                   $mode, $pred, $constraint, $exp, $fuse_type);
+    }
+  }
+
+  my @add_subf_rsubf = ( 'add', 'subf', 'rsubf' );
+
+  # logical-add
+  foreach my $outer_op ( @add_subf_rsubf ) {
+    foreach my $inner_op ( @logicals2 ) {
+      my $exp = logical_addsubf_make_exp ($outer_op, $inner_op, $mode,
+                                          $pred, $constraint);
+      logical_addsubf_emit_define ('logical-add', 'scalar', $outer_op,
+                                   $inner_op, $mode, $pred, $constraint,
+                                   $exp, $fuse_type);
+    }
+  }
+}
+
+# Generate logical/addsubf fusion type for vector.
+sub gen_logical_addsubf_vector
+{
+  my $mode = "VM";
+  my $pred = "altivec_register_operand";
+  my $fuse_type = "fused_vector";
+  my $constraint = "v";
+
+  my @logicals = ( 'and', 'andc', 'eqv', 'nand', 'nor', 'or', 'orc', 'xor' );
+
+  # logical-logical
+  foreach my $outer_op ( @logicals ) {
+    foreach my $inner_op ( @logicals ) {
+      my $exp = logical_addsubf_make_exp ($outer_op, $inner_op, $mode,
+                                          $pred, $constraint);
+      logical_addsubf_emit_define ('logical-logical', 'vector', "v${outer_op}",
+                                   "v${inner_op}", $mode, $pred, $constraint,
+                                   $exp, $fuse_type);
     }
   }
 }

+# Main subroutine to generate logical/addsubf fusion type.
+sub gen_logical_addsubf
+{
+  gen_logical_addsubf_scalar ();
+  gen_logical_addsubf_vector ();
+}
+
+# Main subroutine to generate add-add fusion type.
 sub gen_addadd
 {
-    my ($kind, $vchr, $op, $type, $mode, $pred, $constraint);
+    my ($kind, $op, $type, $mode, $pred, $constraint);
     foreach $kind ('scalar','vector') {
       if ( $kind eq 'vector' ) {
-	  $vchr = "v";
 	  $op = "vaddudm";
 	  $type = "fused_vector";
 	  $mode = "V2DI";
 	  $pred = "altivec_register_operand";
 	  $constraint = "v";
       } else {
-	  $vchr = "";
 	  $op = "add";
 	  $type = "fused_arith_logical";
 	  $mode = "GPR";
@@ -323,9 +439,13 @@  EOF
   }
 }

-gen_ld_cmpi_p10();
-gen_logical_addsubf();
-gen_addadd;
+sub main {
+  gen_copyright_and_top_comments ();
+  gen_ld_cmpi_p10 ();
+  gen_logical_addsubf ();
+  gen_addadd ();
+  return 0;
+}

-exit(0);
+exit main ();