| #!/usr/bin/perl |
| # Generate fusion.md |
| # |
| # Copyright (C) 2020,2021 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/>. |
| |
| use warnings; |
| use strict; |
| |
| print <<'EOF'; |
| ;; Generated automatically by genfusion.pl |
| |
| ;; Copyright (C) 2020,2021 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/>. |
| |
| 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 && TARGET_P10_FUSION_LD_CMPI)\"\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, |
| $target_flag, $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"; |
| $target_flag = "TARGET_P10_FUSION_2LOGICAL"; |
| if ( exists $isaddsub{$outer} ) { |
| @inner_ops = sort keys %logicals_addsub; |
| $ftype = "logical-add"; |
| $target_flag = "TARGET_P10_FUSION_LOGADD"; |
| } elsif ( $kind ne 'vector' && exists $logicals_addsub{$outer} ) { |
| push (@inner_ops, @addsub); |
| } |
| INNER: foreach $inner ( @inner_ops ) { |
| if ( exists $isaddsub{$inner} ) { |
| $ftype = "add-logical"; |
| $target_flag = "TARGET_P10_FUSION_ADDLOG"; |
| } |
| $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,&r"))] |
| "(TARGET_P10_FUSION && $target_flag)" |
| "@ |
| ${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 && TARGET_P10_FUSION_2ADD)" |
| "@ |
| ${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); |
| |