#!/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
# .
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
;; .
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\" \"load\")\n";
print " (set_attr \"cost\" \"8\")\n";
print " (set_attr \"length\" \"8\")])\n";
print "\n";
}
}
}
}
sub gen_2logical
{
my @logicals = ( "and", "andc", "eqv", "nand", "nor", "or", "orc", "xor" );
my %complement = ( "and"=> 0, "andc"=> 1, "eqv"=> 0, "nand"=> 3,
"nor"=> 3, "or"=> 0, "orc"=> 1, "xor"=> 0 );
my %invert = ( "and"=> 0, "andc"=> 0, "eqv"=> 1, "nand"=> 0,
"nor"=> 0, "or"=> 0, "orc"=> 0, "xor"=> 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" );
my ($kind, $vchr, $mode, $pred, $constraint, $cr, $outer, $outer_op,
$outer_comp, $outer_inv, $outer_rtl, $inner, $inner_comp, $inner_inv,
$inner_rtl, $inner_op, $both_commute, $c4, $bc, $inner_arg0,
$inner_arg1, $inner_exp, $outer_arg2, $outer_exp, $insn);
KIND: foreach $kind ('scalar','vector') {
if ( $kind eq 'vector' ) {
$vchr = "v";
$mode = "VM";
$pred = "altivec_register_operand";
$constraint = "v";
} else {
$vchr = "";
$mode = "GPR";
$pred = "gpc_reg_operand";
$constraint = "r";
}
$c4 = "${constraint},${constraint},${constraint},${constraint}";
OUTER: foreach $outer ( @logicals ) {
$outer_op = "${vchr}${outer}";
$outer_comp = $complement{$outer};
$outer_inv = $invert{$outer};
$outer_rtl = $rtlop{$outer};
INNER: foreach $inner ( @logicals ) {
$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)";
}
$outer_exp = "(${outer_rtl}:${mode} ${inner_exp}
${outer_arg2})";
if ( $outer_inv == 1 ) {
$outer_exp = "(not:${mode} $outer_exp)";
}
$insn = <<"EOF";
;; logical-logical fusion pattern generated by gen_2logical
;; $kind $inner_op -> $outer_op
(define_insn "*fuse_${inner_op}_${outer_op}"
[(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_P10_FUSION_2LOGICAL)"
"@
${inner_op} %3,%1,%0\\;${outer_op} %3,%3,%2
${inner_op} %3,%1,%0\\;${outer_op} %3,%3,%2
${inner_op} %3,%1,%0\\;${outer_op} %3,%3,%2
${inner_op} %4,%1,%0\\;${outer_op} %3,%4,%2"
[(set_attr "type" "logical")
(set_attr "cost" "6")
(set_attr "length" "8")])
EOF
print $insn;
}
}
}
}
gen_ld_cmpi_p10();
gen_2logical();
exit(0);