#!/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_one
{
my ($lmode, $result, $ccmode) = @_;
my $np = "NON_PREFIXED_D";
my $mempred = "non_update_memory_operand";
my $extend;
# We need to special case lwa. The prefixed_load_p function in rs6000.cc
# (which determines if a load instruction is prefixed) uses the fact that the
# register mode is different from the memory mode, and that the sign_extend
# attribute is set to use DS-form rules for the address instead of D-form.
# If the register size is the same, prefixed_load_p assumes we are doing a
# lwz. We change to use an lwz and word compare if we don't need to sign
# extend the SImode value. Otherwise if we need the value, we need to
# make sure the insn is marked as ds-form.
my $cmp_size_char = ($lmode eq "SI"
&& $ccmode eq "CC"
&& $result !~ /^EXT|^DI$/) ? "w" : "d";
if ($ccmode eq "CC") {
# ld and lwa are both DS-FORM.
($lmode eq "DI") and $np = "NON_PREFIXED_DS";
($lmode eq "SI" && $cmp_size_char eq "d") and $np = "NON_PREFIXED_DS";
} else {
if ($lmode eq "DI") {
# ld is DS-form, but lwz is not.
$np = "NON_PREFIXED_DS";
}
}
my $cmpl = ($ccmode eq "CC") ? "" : "l";
my $echr = ($ccmode eq "CC" && $cmp_size_char eq "d") ? "a" : "z";
if ($lmode eq "DI") { $echr = ""; }
my $constpred = ($ccmode eq "CC") ? "const_m1_to_1_operand"
: "const_0_to_1_operand";
# For clobber, we need a SI/DI reg in case we
# split because we have to sign/zero extend.
my $clobbermode = ($lmode =~ /^[QH]I$/) ? "GPR" : $lmode;
if ($result =~ /^EXT/ || $result eq "GPR" || $clobbermode eq "GPR") {
# We always need extension if result > lmode.
$extend = ($ccmode eq "CC") ? "sign" : "zero";
} else {
# Result of SI/DI does not need sign extension.
$extend = "none";
}
my $ldst = mode_to_ldst_char($lmode);
# DS-form addresses need YZ, and not m.
my $constraint = ($np eq "NON_PREFIXED_DS") ? "YZ" : "m";
print <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;