#!/usr/bin/perl # Generate fusion.md # # Copyright (C) 2020-2023 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 # . use warnings; use strict; print <<'EOF'; ;; Generated automatically by genfusion.pl ;; Copyright (C) 2020-2023 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 ;; . EOF 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 '?'; } sub gen_ld_cmpi_p10 { 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"; } } } } sub gen_logical_addsubf { 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)"; } $insn = <<"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}"))] "(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}" [(set_attr "type" "$fuse_type") (set_attr "cost" "6") (set_attr "length" "8")]) EOF print $insn; } } } } sub gen_addadd { my ($kind, $vchr, $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"; $pred = "gpc_reg_operand"; $constraint = "r"; } my $c4 = "${constraint},${constraint},${constraint},${constraint}"; print <<"EOF"; ;; ${op}-${op} fusion pattern generated by gen_addadd (define_insn "*fuse_${op}_${op}" [(set (match_operand:${mode} 3 "${pred}" "=&0,&1,&${constraint},${constraint}") (plus:${mode} (plus:${mode} (match_operand:${mode} 0 "${pred}" "${c4}") (match_operand:${mode} 1 "${pred}" "%${c4}")) (match_operand:${mode} 2 "${pred}" "${c4}"))) (clobber (match_scratch:${mode} 4 "=X,X,X,&${constraint}"))] "(TARGET_P10_FUSION)" "@ ${op} %3,%1,%0\\;${op} %3,%3,%2 ${op} %3,%1,%0\\;${op} %3,%3,%2 ${op} %3,%1,%0\\;${op} %3,%3,%2 ${op} %4,%1,%0\\;${op} %3,%4,%2" [(set_attr "type" "${type}") (set_attr "cost" "6") (set_attr "length" "8")]) EOF } } gen_ld_cmpi_p10(); gen_logical_addsubf(); gen_addadd; exit(0);