Canonicalize argument order for commutative functions

This patch uses information about internal functions to canonicalize
the argument order of calls.

gcc/
	* gimple-fold.c: Include internal-fn.h.
	(fold_stmt_1): If a function maps to an internal one, use
	first_commutative_argument to canonicalize the order of
	commutative arguments.
	* gimple-match-head.c (gimple_resimplify2, gimple_resimplify3)
	(gimple_resimplify4, gimple_resimplify5): Extend commutativity
	checks to functions.

gcc/testsuite/
	* gcc.dg/fmax-fmin-1.c: New test.
This commit is contained in:
Richard Sandiford 2021-11-30 09:52:25 +00:00
parent e32b9eb32d
commit e9fff24cd2
3 changed files with 75 additions and 20 deletions

View File

@ -69,6 +69,7 @@ along with GCC; see the file COPYING3. If not see
#include "varasm.h"
#include "memmodel.h"
#include "optabs.h"
#include "internal-fn.h"
enum strlen_range_kind {
/* Compute the exact constant string length. */
@ -6109,18 +6110,36 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree))
break;
case GIMPLE_CALL:
{
for (i = 0; i < gimple_call_num_args (stmt); ++i)
gcall *call = as_a<gcall *> (stmt);
for (i = 0; i < gimple_call_num_args (call); ++i)
{
tree *arg = gimple_call_arg_ptr (stmt, i);
tree *arg = gimple_call_arg_ptr (call, i);
if (REFERENCE_CLASS_P (*arg)
&& maybe_canonicalize_mem_ref_addr (arg))
changed = true;
}
tree *lhs = gimple_call_lhs_ptr (stmt);
tree *lhs = gimple_call_lhs_ptr (call);
if (*lhs
&& REFERENCE_CLASS_P (*lhs)
&& maybe_canonicalize_mem_ref_addr (lhs))
changed = true;
if (*lhs)
{
combined_fn cfn = gimple_call_combined_fn (call);
internal_fn ifn = associated_internal_fn (cfn, TREE_TYPE (*lhs));
int opno = first_commutative_argument (ifn);
if (opno >= 0)
{
tree arg1 = gimple_call_arg (call, opno);
tree arg2 = gimple_call_arg (call, opno + 1);
if (tree_swap_operands_p (arg1, arg2))
{
gimple_call_set_arg (call, opno, arg2);
gimple_call_set_arg (call, opno + 1, arg1);
changed = true;
}
}
}
break;
}
case GIMPLE_ASM:

View File

@ -294,18 +294,16 @@ gimple_resimplify2 (gimple_seq *seq, gimple_match_op *res_op,
/* Canonicalize operand order. */
bool canonicalized = false;
if (res_op->code.is_tree_code ())
bool is_comparison
= (res_op->code.is_tree_code ()
&& TREE_CODE_CLASS (tree_code (res_op->code)) == tcc_comparison);
if ((is_comparison || commutative_binary_op_p (res_op->code, res_op->type))
&& tree_swap_operands_p (res_op->ops[0], res_op->ops[1]))
{
auto code = tree_code (res_op->code);
if ((TREE_CODE_CLASS (code) == tcc_comparison
|| commutative_tree_code (code))
&& tree_swap_operands_p (res_op->ops[0], res_op->ops[1]))
{
std::swap (res_op->ops[0], res_op->ops[1]);
if (TREE_CODE_CLASS (code) == tcc_comparison)
res_op->code = swap_tree_comparison (code);
canonicalized = true;
}
std::swap (res_op->ops[0], res_op->ops[1]);
if (is_comparison)
res_op->code = swap_tree_comparison (tree_code (res_op->code));
canonicalized = true;
}
/* Limit recursion, see gimple_resimplify1. */
@ -376,11 +374,11 @@ gimple_resimplify3 (gimple_seq *seq, gimple_match_op *res_op,
/* Canonicalize operand order. */
bool canonicalized = false;
if (res_op->code.is_tree_code ()
&& commutative_ternary_tree_code (tree_code (res_op->code))
&& tree_swap_operands_p (res_op->ops[0], res_op->ops[1]))
int argno = first_commutative_argument (res_op->code, res_op->type);
if (argno >= 0
&& tree_swap_operands_p (res_op->ops[argno], res_op->ops[argno + 1]))
{
std::swap (res_op->ops[0], res_op->ops[1]);
std::swap (res_op->ops[argno], res_op->ops[argno + 1]);
canonicalized = true;
}
@ -424,6 +422,16 @@ gimple_resimplify4 (gimple_seq *seq, gimple_match_op *res_op,
{
/* No constant folding is defined for four-operand functions. */
/* Canonicalize operand order. */
bool canonicalized = false;
int argno = first_commutative_argument (res_op->code, res_op->type);
if (argno >= 0
&& tree_swap_operands_p (res_op->ops[argno], res_op->ops[argno + 1]))
{
std::swap (res_op->ops[argno], res_op->ops[argno + 1]);
canonicalized = true;
}
/* Limit recursion, see gimple_resimplify1. */
static unsigned depth;
if (depth > 10)
@ -450,7 +458,7 @@ gimple_resimplify4 (gimple_seq *seq, gimple_match_op *res_op,
if (maybe_resimplify_conditional_op (seq, res_op, valueize))
return true;
return false;
return canonicalized;
}
/* Helper that matches and simplifies the toplevel result from
@ -465,6 +473,16 @@ gimple_resimplify5 (gimple_seq *seq, gimple_match_op *res_op,
{
/* No constant folding is defined for five-operand functions. */
/* Canonicalize operand order. */
bool canonicalized = false;
int argno = first_commutative_argument (res_op->code, res_op->type);
if (argno >= 0
&& tree_swap_operands_p (res_op->ops[argno], res_op->ops[argno + 1]))
{
std::swap (res_op->ops[argno], res_op->ops[argno + 1]);
canonicalized = true;
}
gimple_match_op res_op2 (*res_op);
if (gimple_simplify (&res_op2, seq, valueize,
res_op->code, res_op->type,
@ -478,7 +496,7 @@ gimple_resimplify5 (gimple_seq *seq, gimple_match_op *res_op,
if (maybe_resimplify_conditional_op (seq, res_op, valueize))
return true;
return false;
return canonicalized;
}
/* Match and simplify the toplevel valueized operation THIS.

View File

@ -0,0 +1,18 @@
/* { dg-options "-O -fdump-tree-optimized" } */
void
f1 (double *res, double x, double y)
{
res[0] = __builtin_fmax (x, y);
res[1] = __builtin_fmax (y, x);
}
void
f2 (double *res, double x, double y)
{
res[0] = __builtin_fmin (x, y);
res[1] = __builtin_fmin (y, x);
}
/* { dg-final { scan-tree-dump-times {__builtin_fmax} 1 "optimized" } } */
/* { dg-final { scan-tree-dump-times {__builtin_fmin} 1 "optimized" } } */