Commit 4307a485 by Feng Xue Committed by Feng Xue

Generalized IPA predicate on parameter reference

2019-10-16  Feng Xue  <fxue@os.amperecomputing.com>

        PR ipa/91088
        * doc/invoke.texi (ipa-max-param-expr-ops): Document new option.
        * params.def (PARAM_IPA_MAX_PARAM_EXPR_OPS): New.
        * ipa-predicat.h (struct expr_eval_op): New struct.
        (expr_eval_ops): New typedef.
        (struct condition): Add type and param_ops fields, remove size field.
        (add_condition): Replace size parameter with type parameter, add
        param_ops parameter.
        * ipa-predicat.c (expr_eval_ops_equal_p): New function.
        (predicate::add_clause): Add comparisons on type and param_ops.
        (dump_condition): Add debug dump for param_ops.
        (remap_after_inlining): Adjust call arguments to add_condition.
        (add_condition): Replace size parameter with type parameter, add
        param_ops parameter. Unshare constant value used in conditions.
        * ipa-fnsummary.c (evaluate_conditions_for_known_args): Fold
        parameter expressions using param_ops.
        (decompose_param_expr):  New function.
        (set_cond_stmt_execution_predicate): Use call to decompose_param_expr
        to replace call to unmodified_parm_or_parm_agg_item.
        (set_switch_stmt_execution_predicate): Likewise.
        (will_be_nonconstant_expr_predicate): Likewise. Replace usage of size
        with type.
        (inline_read_section): Read param_ops from summary stream.
        (ipa_fn_summary_write): Write param_ops to summary stream.

2019-10-16  Feng Xue  <fxue@os.amperecomputing.com>

        PR ipa/91088
        * gcc.dg/ipa/pr91088.c: New test.
        * gcc.dg/ipa/pr91089.c: Add sub-test for range analysis.
        * g++.dg/tree-ssa/ivopts-3.C: Force a function to be noinline.

From-SVN: r277054
parent 8cf9bbd2
2019-10-16 Feng Xue <fxue@os.amperecomputing.com>
PR ipa/91088
* doc/invoke.texi (ipa-max-param-expr-ops): Document new option.
* params.def (PARAM_IPA_MAX_PARAM_EXPR_OPS): New.
* ipa-predicat.h (struct expr_eval_op): New struct.
(expr_eval_ops): New typedef.
(struct condition): Add type and param_ops fields, remove size field.
(add_condition): Replace size parameter with type parameter, add
param_ops parameter.
* ipa-predicat.c (expr_eval_ops_equal_p): New function.
(predicate::add_clause): Add comparisons on type and param_ops.
(dump_condition): Add debug dump for param_ops.
(remap_after_inlining): Adjust call arguments to add_condition.
(add_condition): Replace size parameter with type parameter, add
param_ops parameter. Unshare constant value used in conditions.
* ipa-fnsummary.c (evaluate_conditions_for_known_args): Fold
parameter expressions using param_ops.
(decompose_param_expr): New function.
(set_cond_stmt_execution_predicate): Use call to decompose_param_expr
to replace call to unmodified_parm_or_parm_agg_item.
(set_switch_stmt_execution_predicate): Likewise.
(will_be_nonconstant_expr_predicate): Likewise. Replace usage of size
with type.
(inline_read_section): Read param_ops from summary stream.
(ipa_fn_summary_write): Write param_ops to summary stream.
2019-10-15 Segher Boessenkool <segher@kernel.crashing.org> 2019-10-15 Segher Boessenkool <segher@kernel.crashing.org>
PR rtl-optimization/92107 PR rtl-optimization/92107
......
...@@ -12034,6 +12034,13 @@ For switch exceeding this limit, IPA-CP will not construct cloning cost ...@@ -12034,6 +12034,13 @@ For switch exceeding this limit, IPA-CP will not construct cloning cost
predicate, which is used to estimate cloning benefit, for default case predicate, which is used to estimate cloning benefit, for default case
of the switch statement. of the switch statement.
@item ipa-max-param-expr-ops
IPA-CP will analyze conditional statement that references some function
parameter to estimate benefit for cloning upon certain constant value.
But if number of operations in a parameter expression exceeds
@option{ipa-max-param-expr-ops}, the expression is treated as complicated
one, and is not handled by IPA analysis.
@item lto-partitions @item lto-partitions
Specify desired number of partitions produced during WHOPR compilation. Specify desired number of partitions produced during WHOPR compilation.
The number of partitions should exceed the number of CPUs used for compilation. The number of partitions should exceed the number of CPUs used for compilation.
...@@ -33,9 +33,36 @@ along with GCC; see the file COPYING3. If not see ...@@ -33,9 +33,36 @@ along with GCC; see the file COPYING3. If not see
#include "fold-const.h" #include "fold-const.h"
#include "tree-pretty-print.h" #include "tree-pretty-print.h"
#include "gimple.h" #include "gimple.h"
#include "gimplify.h"
#include "data-streamer.h" #include "data-streamer.h"
/* Check whether two set of operations have same effects. */
static bool
expr_eval_ops_equal_p (expr_eval_ops ops1, expr_eval_ops ops2)
{
if (ops1)
{
if (!ops2 || ops1->length () != ops2->length ())
return false;
for (unsigned i = 0; i < ops1->length (); i++)
{
expr_eval_op &op1 = (*ops1)[i];
expr_eval_op &op2 = (*ops2)[i];
if (op1.code != op2.code
|| op1.index != op2.index
|| !vrp_operand_equal_p (op1.val[0], op2.val[0])
|| !vrp_operand_equal_p (op1.val[1], op2.val[1])
|| !types_compatible_p (op1.type, op2.type))
return false;
}
return true;
}
return !ops2;
}
/* Add clause CLAUSE into the predicate P. /* Add clause CLAUSE into the predicate P.
When CONDITIONS is NULL do not perform checking whether NEW_CLAUSE When CONDITIONS is NULL do not perform checking whether NEW_CLAUSE
is obviously true. This is useful only when NEW_CLAUSE is known to be is obviously true. This is useful only when NEW_CLAUSE is known to be
...@@ -110,14 +137,16 @@ predicate::add_clause (conditions conditions, clause_t new_clause) ...@@ -110,14 +137,16 @@ predicate::add_clause (conditions conditions, clause_t new_clause)
for (c2 = c1 + 1; c2 < num_conditions; c2++) for (c2 = c1 + 1; c2 < num_conditions; c2++)
if (new_clause & (1 << c2)) if (new_clause & (1 << c2))
{ {
condition *cc1 =
&(*conditions)[c1 - predicate::first_dynamic_condition];
condition *cc2 = condition *cc2 =
&(*conditions)[c2 - predicate::first_dynamic_condition]; &(*conditions)[c2 - predicate::first_dynamic_condition];
if (cc1->operand_num == cc2->operand_num if (cc1->operand_num == cc2->operand_num
&& cc1->val == cc2->val && vrp_operand_equal_p (cc1->val, cc2->val)
&& cc2->code != is_not_constant && cc2->code != is_not_constant
&& cc2->code != predicate::changed && cc2->code != changed
&& expr_eval_ops_equal_p (cc1->param_ops, cc2->param_ops)
&& cc2->agg_contents == cc1->agg_contents
&& cc2->by_ref == cc1->by_ref
&& types_compatible_p (cc2->type, cc1->type)
&& cc1->code == invert_tree_comparison (cc2->code, && cc1->code == invert_tree_comparison (cc2->code,
HONOR_NANS (cc1->val))) HONOR_NANS (cc1->val)))
return; return;
...@@ -300,6 +329,83 @@ dump_condition (FILE *f, conditions conditions, int cond) ...@@ -300,6 +329,83 @@ dump_condition (FILE *f, conditions conditions, int cond)
if (c->agg_contents) if (c->agg_contents)
fprintf (f, "[%soffset: " HOST_WIDE_INT_PRINT_DEC "]", fprintf (f, "[%soffset: " HOST_WIDE_INT_PRINT_DEC "]",
c->by_ref ? "ref " : "", c->offset); c->by_ref ? "ref " : "", c->offset);
for (unsigned i = 0; i < vec_safe_length (c->param_ops); i++)
{
expr_eval_op &op = (*(c->param_ops))[i];
const char *op_name = op_symbol_code (op.code);
if (op_name == op_symbol_code (ERROR_MARK))
op_name = get_tree_code_name (op.code);
fprintf (f, ",(");
if (!op.val[0])
{
switch (op.code)
{
case FLOAT_EXPR:
case FIX_TRUNC_EXPR:
case FIXED_CONVERT_EXPR:
case VIEW_CONVERT_EXPR:
CASE_CONVERT:
if (op.code == VIEW_CONVERT_EXPR)
fprintf (f, "VCE");
fprintf (f, "(");
print_generic_expr (f, op.type);
fprintf (f, ")" );
break;
default:
fprintf (f, "%s", op_name);
}
fprintf (f, " #");
}
else if (!op.val[1])
{
if (op.index)
{
print_generic_expr (f, op.val[0]);
fprintf (f, " %s #", op_name);
}
else
{
fprintf (f, "# %s ", op_name);
print_generic_expr (f, op.val[0]);
}
}
else
{
fprintf (f, "%s ", op_name);
switch (op.index)
{
case 0:
fprintf (f, "#, ");
print_generic_expr (f, op.val[0]);
fprintf (f, ", ");
print_generic_expr (f, op.val[1]);
break;
case 1:
print_generic_expr (f, op.val[0]);
fprintf (f, ", #, ");
print_generic_expr (f, op.val[1]);
break;
case 2:
print_generic_expr (f, op.val[0]);
fprintf (f, ", ");
print_generic_expr (f, op.val[1]);
fprintf (f, ", #");
break;
default:
fprintf (f, "*, *, *");
}
}
fprintf (f, ")");
}
if (c->code == predicate::is_not_constant) if (c->code == predicate::is_not_constant)
{ {
fprintf (f, " not constant"); fprintf (f, " not constant");
...@@ -462,8 +568,8 @@ predicate::remap_after_inlining (class ipa_fn_summary *info, ...@@ -462,8 +568,8 @@ predicate::remap_after_inlining (class ipa_fn_summary *info,
ap.by_ref = c->by_ref; ap.by_ref = c->by_ref;
cond_predicate = add_condition (info, cond_predicate = add_condition (info,
operand_map[c->operand_num], operand_map[c->operand_num],
c->size, &ap, c->code, c->type, &ap, c->code,
c->val); c->val, c->param_ops);
} }
} }
/* Fixed conditions remains same, construct single /* Fixed conditions remains same, construct single
...@@ -516,21 +622,23 @@ predicate::stream_out (struct output_block *ob) ...@@ -516,21 +622,23 @@ predicate::stream_out (struct output_block *ob)
} }
/* Add condition to condition list SUMMARY. OPERAND_NUM, SIZE, CODE and VAL /* Add condition to condition list SUMMARY. OPERAND_NUM, TYPE, CODE, VAL and
correspond to fields of condition structure. AGGPOS describes whether the PARAM_OPS correspond to fields of condition structure. AGGPOS describes
used operand is loaded from an aggregate and where in the aggregate it is. whether the used operand is loaded from an aggregate and where in the
It can be NULL, which means this not a load from an aggregate. */ aggregate it is. It can be NULL, which means this not a load from an
aggregate. */
predicate predicate
add_condition (class ipa_fn_summary *summary, int operand_num, add_condition (class ipa_fn_summary *summary, int operand_num,
poly_int64 size, struct agg_position_info *aggpos, tree type, struct agg_position_info *aggpos,
enum tree_code code, tree val) enum tree_code code, tree val, expr_eval_ops param_ops)
{ {
int i; int i, j;
struct condition *c; struct condition *c;
struct condition new_cond; struct condition new_cond;
HOST_WIDE_INT offset; HOST_WIDE_INT offset;
bool agg_contents, by_ref; bool agg_contents, by_ref;
expr_eval_op *op;
if (aggpos) if (aggpos)
{ {
...@@ -549,10 +657,11 @@ add_condition (class ipa_fn_summary *summary, int operand_num, ...@@ -549,10 +657,11 @@ add_condition (class ipa_fn_summary *summary, int operand_num,
for (i = 0; vec_safe_iterate (summary->conds, i, &c); i++) for (i = 0; vec_safe_iterate (summary->conds, i, &c); i++)
{ {
if (c->operand_num == operand_num if (c->operand_num == operand_num
&& known_eq (c->size, size)
&& c->code == code && c->code == code
&& c->val == val && types_compatible_p (c->type, type)
&& vrp_operand_equal_p (c->val, val)
&& c->agg_contents == agg_contents && c->agg_contents == agg_contents
&& expr_eval_ops_equal_p (c->param_ops, param_ops)
&& (!agg_contents || (c->offset == offset && c->by_ref == by_ref))) && (!agg_contents || (c->offset == offset && c->by_ref == by_ref)))
return predicate::predicate_testing_cond (i); return predicate::predicate_testing_cond (i);
} }
...@@ -562,11 +671,21 @@ add_condition (class ipa_fn_summary *summary, int operand_num, ...@@ -562,11 +671,21 @@ add_condition (class ipa_fn_summary *summary, int operand_num,
new_cond.operand_num = operand_num; new_cond.operand_num = operand_num;
new_cond.code = code; new_cond.code = code;
new_cond.val = val; new_cond.type = unshare_expr_without_location (type);
new_cond.val = val ? unshare_expr_without_location (val) : val;
new_cond.agg_contents = agg_contents; new_cond.agg_contents = agg_contents;
new_cond.by_ref = by_ref; new_cond.by_ref = by_ref;
new_cond.offset = offset; new_cond.offset = offset;
new_cond.size = size; new_cond.param_ops = vec_safe_copy (param_ops);
for (j = 0; vec_safe_iterate (new_cond.param_ops, j, &op); j++)
{
if (op->val[0])
op->val[0] = unshare_expr_without_location (op->val[0]);
if (op->val[1])
op->val[1] = unshare_expr_without_location (op->val[1]);
}
vec_safe_push (summary->conds, new_cond); vec_safe_push (summary->conds, new_cond);
return predicate::predicate_testing_cond (i); return predicate::predicate_testing_cond (i);
......
...@@ -22,16 +22,36 @@ along with GCC; see the file COPYING3. If not see ...@@ -22,16 +22,36 @@ along with GCC; see the file COPYING3. If not see
inlined into (i.e. known constant values of function parameters. inlined into (i.e. known constant values of function parameters.
Conditions that are interesting for function body are collected into CONDS Conditions that are interesting for function body are collected into CONDS
vector. They are of simple for function_param OP VAL, where VAL is vector. They are of simple as kind of a mathematical transformation on
IPA invariant. The conditions are then referred by predicates. */ function parameter, T(function_param), in which the parameter occurs only
once, and other operands are IPA invariant. The conditions are then
referred by predicates. */
/* A simplified representation of tree node, for unary, binary and ternary
operation. Computations on parameter are decomposed to a series of this
kind of structure. */
struct GTY(()) expr_eval_op
{
/* Result type of expression. */
tree type;
/* Constant operands in expression, there are at most two. */
tree val[2];
/* Index of parameter operand in expression. */
unsigned index : 2;
/* Operation code of expression. */
ENUM_BITFIELD(tree_code) code : 16;
};
typedef vec<expr_eval_op, va_gc> *expr_eval_ops;
struct GTY(()) condition struct GTY(()) condition
{ {
/* If agg_contents is set, this is the offset from which the used data was /* If agg_contents is set, this is the offset from which the used data was
loaded. */ loaded. */
HOST_WIDE_INT offset; HOST_WIDE_INT offset;
/* Size of the access reading the data (or the PARM_DECL SSA_NAME). */ /* Type of the access reading the data (or the PARM_DECL SSA_NAME). */
poly_int64 size; tree type;
tree val; tree val;
int operand_num; int operand_num;
ENUM_BITFIELD(tree_code) code : 16; ENUM_BITFIELD(tree_code) code : 16;
...@@ -41,6 +61,9 @@ struct GTY(()) condition ...@@ -41,6 +61,9 @@ struct GTY(()) condition
/* If agg_contents is set, this differentiates between loads from data /* If agg_contents is set, this differentiates between loads from data
passed by reference and by value. */ passed by reference and by value. */
unsigned by_ref : 1; unsigned by_ref : 1;
/* A set of sequential operations on the parameter, which can be seen as
a mathmatical function on the parameter. */
expr_eval_ops param_ops;
}; };
/* Information kept about parameter of call site. */ /* Information kept about parameter of call site. */
...@@ -228,5 +251,6 @@ private: ...@@ -228,5 +251,6 @@ private:
void dump_condition (FILE *f, conditions conditions, int cond); void dump_condition (FILE *f, conditions conditions, int cond);
predicate add_condition (class ipa_fn_summary *summary, int operand_num, predicate add_condition (class ipa_fn_summary *summary, int operand_num,
poly_int64 size, struct agg_position_info *aggpos, tree type, struct agg_position_info *aggpos,
enum tree_code code, tree val); enum tree_code code, tree val,
expr_eval_ops param_ops = NULL);
...@@ -1165,6 +1165,12 @@ DEFPARAM (PARAM_IPA_MAX_SWITCH_PREDICATE_BOUNDS, ...@@ -1165,6 +1165,12 @@ DEFPARAM (PARAM_IPA_MAX_SWITCH_PREDICATE_BOUNDS,
"statement used during IPA functoin summary generation.", "statement used during IPA functoin summary generation.",
5, 0, 0) 5, 0, 0)
DEFPARAM (PARAM_IPA_MAX_PARAM_EXPR_OPS,
"ipa-max-param-expr-ops",
"Maximum number of operations in a parameter expression that can "
"be handled by IPA analysis.",
10, 0, 0)
/* WHOPR partitioning configuration. */ /* WHOPR partitioning configuration. */
DEFPARAM (PARAM_LTO_PARTITIONS, DEFPARAM (PARAM_LTO_PARTITIONS,
......
2019-10-16 Feng Xue <fxue@os.amperecomputing.com>
PR ipa/91088
* gcc.dg/ipa/pr91088.c: New test.
* gcc.dg/ipa/pr91089.c: Add sub-test for range analysis.
* g++.dg/tree-ssa/ivopts-3.C: Force a function to be noinline.
2019-10-15 Andrew Pinski <apinski@marvell.com> 2019-10-15 Andrew Pinski <apinski@marvell.com>
* gcc.c-torture/compile/20191015-1.c: New test. * gcc.c-torture/compile/20191015-1.c: New test.
......
...@@ -25,7 +25,7 @@ protected: ...@@ -25,7 +25,7 @@ protected:
double stuff; double stuff;
public: public:
explicit MinimalVector ( int length ) { __attribute__((noinline)) explicit MinimalVector ( int length ) {
_pData = new double[length]; _pData = new double[length];
for (int i = 0; i < length; ++i) _pData[i] = 0.; for (int i = 0; i < length; ++i) _pData[i] = 0.;
} }
......
/* { dg-do compile } */
/* { dg-options "-O3 -fdump-ipa-cp-details -fno-inline" } */
int foo();
#define large_code \
do { \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
foo (); \
} while (1)
struct A
{
char f1;
short f2 : 5;
int f3;
};
int callee1 (struct A a)
{
if ((a.f2 + 7) & 17)
foo ();
if ((1300 / (short)a.f3) == 19)
large_code;
return 1;
}
int callee2 (short *p)
{
if ((*p ^ 1) < 8)
large_code;
return 2;
}
int callee3 (int v)
{
if ((27 % ((1 - (char) v) * 3)) < 6)
{
large_code;
return v + 2;
}
else
return v + 1;
}
int caller ()
{
struct A a;
short b;
a.f2 = -7;
a.f3 = 68;
if (callee1 (a))
foo ();
a.f2 = 3;
a.f3 = 10;
if (callee1 (a))
foo ();
b = 9;
if (callee2 (&b))
foo ();
b = 2;
if (callee2 (&b))
foo ();
return callee3 (-5) +
callee3 (0);
}
/* { dg-final { scan-ipa-dump-times "Creating a specialized node of callee1" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "Creating a specialized node of callee2" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "Creating a specialized node of callee3" 1 "cp" } } */
/* { dg-final { scan-ipa-dump "op0\\\[offset: 32],\\(\\(short int\\) #\\),\\(\\(int\\) #\\),\\(1300 / #\\) == 19" "cp" } } */
/* { dg-final { scan-ipa-dump "op0\\\[ref offset: 0],\\(# \\^ 1\\) <" "cp" } } */
/* { dg-final { scan-ipa-dump "op0,\\(\\(char\\) #\\),\\(\\(int\\) #\\),\\(1 - #\\),\\(# \\* 3\\),\\(27 % #\\) <" "cp" } } */
...@@ -7,7 +7,7 @@ int data; ...@@ -7,7 +7,7 @@ int data;
int callee (int i) int callee (int i)
{ {
switch (i) switch (i % 128)
{ {
case -126: return i + 13; case -126: return i + 13;
case -127: return i + 5; case -127: return i + 5;
...@@ -45,7 +45,7 @@ int fn2 (); ...@@ -45,7 +45,7 @@ int fn2 ();
int callee_complex_predicate (int i) int callee_complex_predicate (int i)
{ {
switch (i ) switch (i)
{ {
case 0: case 0:
fn (); fn ();
...@@ -100,10 +100,10 @@ int caller () ...@@ -100,10 +100,10 @@ int caller ()
} }
/* { dg-final { scan-ipa-dump-times "Creating a specialized node of callee" 7 "cp" } } */ /* { dg-final { scan-ipa-dump-times "Creating a specialized node of callee" 7 "cp" } } */
/* { dg-final { scan-ipa-dump "op0 < -127" "fnsummary" } } */ /* { dg-final { scan-ipa-dump-not "op0,\\(# % 128\\) < -127" "fnsummary" } } */
/* { dg-final { scan-ipa-dump "op0 > -126" "fnsummary" } } */ /* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) > -126" "fnsummary" } } */
/* { dg-final { scan-ipa-dump "op0 != -8" "fnsummary" } } */ /* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) != -8" "fnsummary" } } */
/* { dg-final { scan-ipa-dump "op0 != 0" "fnsummary" } } */ /* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) != 0" "fnsummary" } } */
/* { dg-final { scan-ipa-dump "op0 < 5" "fnsummary" } } */ /* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) < 5" "fnsummary" } } */
/* { dg-final { scan-ipa-dump "op0 > 7" "fnsummary" } } */ /* { dg-final { scan-ipa-dump "op0,\\(# % 128\\) > 7" "fnsummary" } } */
/* { dg-final { scan-ipa-dump "loop depth: 1 .+ time:\[ \]*\[0-9\]+ predicate: \\(op0 == 1000\\)\[\r\n]+" "fnsummary" } } */ /* { dg-final { scan-ipa-dump "loop depth: 1 .+ time:\[ \]*\[0-9\]+ predicate: \\(op0 == 1000\\)\[\r\n]+" "fnsummary" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment