Use known value ranges while evaluating ipa predicates
diff mbox series

Message ID 20191112130332.b5dyohbxz7s4k4op@kam.mff.cuni.cz
State New
Headers show
Series
  • Use known value ranges while evaluating ipa predicates
Related show

Commit Message

Jan Hubicka Nov. 12, 2019, 1:03 p.m. UTC
Hi,
this implements use of value ranges in ipa-predicates so inliner know
when some tests are going to be removed (especially NULL pointer
checks).

Bootstrapped/regtested x86_64-linux. Martin, I would apprechiate if you
look on the patch. 

Honza

	* ipa-cp.c (ipa_vr_operation_and_type_effects): Move up in file.
	(ipa_value_range_from_jfunc): New function.
	* ipa-fnsummary.c (evaluate_conditions_for_known_args): Add
	known_value_ranges parameter; use it to evalulate conditions.
	(evaluate_properties_for_edge): Compute known value ranges.
	(ipa_fn_summary_t::duplicate): Update use of
	evaluate_conditions_for_known_args.
	(estimate_ipcp_clone_size_and_time): Likewise.
	(ipa_merge_fn_summary_after_inlining): Likewise.
	* ipa-prop.h (ipa_value_range_from_jfunc): Declare.
	* gcc.dg/ipa/inline-9.c: New testcase.

Comments

Jakub Jelinek Nov. 14, 2019, 11:46 p.m. UTC | #1
On Tue, Nov 12, 2019 at 02:03:32PM +0100, Jan Hubicka wrote:
> this implements use of value ranges in ipa-predicates so inliner know
> when some tests are going to be removed (especially NULL pointer
> checks).
> 
> Bootstrapped/regtested x86_64-linux. Martin, I would apprechiate if you
> look on the patch. 

> 	* gcc.dg/ipa/inline-9.c: New testcase.

The testcase is now UNRESOLVED on both x86_64-linux and i686-linux:

> --- testsuite/gcc.dg/ipa/inline-9.c	(nonexistent)
> +++ testsuite/gcc.dg/ipa/inline-9.c	(working copy)
> @@ -0,0 +1,21 @@
> +/* { dg-options "-Os -fdump-ipa-inline"  } */
> +int test(int a)
> +{
> + if (a>100)
> +   {
> +     foo();
> +     foo();
> +     foo();
> +     foo();
> +     foo();
> +     foo();
> +     foo();
> +     foo();
> +   }
> +}
> +main()
> +{
> +  for (int i=0;i<100;i++)
> +    test(i);
> +}
> +/* { dg-final { scan-tree-dump "Inlined 1 calls" "inline" } } */

PASS: gcc.dg/ipa/inline-9.c (test for excess errors)
gcc.dg/ipa/inline-9.c: dump file does not exist
UNRESOLVED: gcc.dg/ipa/inline-9.c scan-tree-dump inline "Inlined 1 calls"

but fixing the obvious bug in there, s/scan-tree-dump/scan-ipa-dump/
doesn't help, nothing is really inlined.

	Jakub
Jan Hubicka Nov. 15, 2019, 8:18 a.m. UTC | #2
> PASS: gcc.dg/ipa/inline-9.c (test for excess errors)
> gcc.dg/ipa/inline-9.c: dump file does not exist
> UNRESOLVED: gcc.dg/ipa/inline-9.c scan-tree-dump inline "Inlined 1 calls"
> 
> but fixing the obvious bug in there, s/scan-tree-dump/scan-ipa-dump/
> doesn't help, nothing is really inlined.

It gets inlined, but need -details to get the message.
I remember correcting this testcase before, but I must have used old
copy in the patch. Sorry for that.

The following makes testcase to pass.

Honza

	* gcc.dg/ipa/inline-9.c: Fix template.
Index: testsuite/gcc.dg/ipa/inline-9.c
===================================================================
--- testsuite/gcc.dg/ipa/inline-9.c	(revision 278222)
+++ testsuite/gcc.dg/ipa/inline-9.c	(working copy)
@@ -1,4 +1,4 @@
-/* { dg-options "-Os -fdump-ipa-inline"  } */
+/* { dg-options "-Os -fdump-ipa-inline-details"  } */
 int foo (void);
 int test(int a)
 {
@@ -20,4 +20,4 @@ main()
   for (int i=0;i<100;i++)
     test(i);
 }
-/* { dg-final { scan-tree-dump "Inlined 1 calls" "inline" } } */
+/* { dg-final { scan-ipa-dump "Inlined 1 calls" "inline" } } */

Patch
diff mbox series

Index: ipa-cp.c
===================================================================
--- ipa-cp.c	(revision 278094)
+++ ipa-cp.c	(working copy)
@@ -1459,6 +1459,87 @@  ipa_context_from_jfunc (ipa_node_params
   return ctx;
 }
 
+/* Emulate effects of unary OPERATION and/or conversion from SRC_TYPE to
+   DST_TYPE on value range in SRC_VR and store it to DST_VR.  Return true if
+   the result is a range or an anti-range.  */
+
+static bool
+ipa_vr_operation_and_type_effects (value_range *dst_vr,
+				   value_range *src_vr,
+				   enum tree_code operation,
+				   tree dst_type, tree src_type)
+{
+  range_fold_unary_expr (dst_vr, operation, dst_type, src_vr, src_type);
+  if (dst_vr->varying_p () || dst_vr->undefined_p ())
+    return false;
+  return true;
+}
+
+/* Determine value_range of JFUNC given that INFO describes the caller node or
+   the one it is inlined to, CS is the call graph edge corresponding to JFUNC
+   and PARM_TYPE of the parameter.  */
+
+value_range
+ipa_value_range_from_jfunc (ipa_node_params *info, cgraph_edge *cs,
+			    ipa_jump_func *jfunc, tree parm_type)
+{
+  value_range vr;
+  return vr;
+  if (jfunc->m_vr)
+    ipa_vr_operation_and_type_effects (&vr,
+				       jfunc->m_vr,
+				       NOP_EXPR, parm_type,
+				       jfunc->m_vr->type ());
+  if (vr.singleton_p ())
+    return vr;
+  if (jfunc->type == IPA_JF_PASS_THROUGH)
+    {
+      int idx;
+      ipcp_transformation *sum
+	= ipcp_get_transformation_summary (cs->caller->inlined_to
+					   ? cs->caller->inlined_to
+					   : cs->caller);
+      if (!sum || !sum->m_vr)
+	return vr;
+
+      idx = ipa_get_jf_pass_through_formal_id (jfunc);
+
+      if (!(*sum->m_vr)[idx].known)
+	return vr;
+      tree vr_type = ipa_get_type (info, idx);
+      value_range srcvr ((*sum->m_vr)[idx].type,
+			 wide_int_to_tree (vr_type, (*sum->m_vr)[idx].min),
+			 wide_int_to_tree (vr_type, (*sum->m_vr)[idx].max));
+
+      enum tree_code operation = ipa_get_jf_pass_through_operation (jfunc);
+
+      if (TREE_CODE_CLASS (operation) == tcc_unary)
+	{
+	  value_range res;
+
+	  if (ipa_vr_operation_and_type_effects (&res,
+						 &srcvr,
+						 operation, parm_type,
+						 vr_type))
+	    vr.intersect (res);
+	}
+      else
+	{
+	  value_range op_res, res;
+	  tree op = ipa_get_jf_pass_through_operand (jfunc);
+	  value_range op_vr (op, op);
+
+	  range_fold_binary_expr (&op_res, operation, vr_type, &srcvr, &op_vr);
+	  if (ipa_vr_operation_and_type_effects (&res,
+						 &op_res,
+						 NOP_EXPR, parm_type,
+						 vr_type))
+	    vr.intersect (res);
+	}
+    }
+  return vr;
+}
+
 /* If checking is enabled, verify that no lattice is in the TOP state, i.e. not
    bottom, not containing a variable component and without any known value at
    the same time.  */
@@ -1936,22 +2017,6 @@  propagate_bits_across_jump_function (cgr
     return dest_lattice->set_to_bottom ();
 }
 
-/* Emulate effects of unary OPERATION and/or conversion from SRC_TYPE to
-   DST_TYPE on value range in SRC_VR and store it to DST_VR.  Return true if
-   the result is a range or an anti-range.  */
-
-static bool
-ipa_vr_operation_and_type_effects (value_range *dst_vr,
-				   value_range *src_vr,
-				   enum tree_code operation,
-				   tree dst_type, tree src_type)
-{
-  range_fold_unary_expr (dst_vr, operation, dst_type, src_vr, src_type);
-  if (dst_vr->varying_p () || dst_vr->undefined_p ())
-    return false;
-  return true;
-}
-
 /* Propagate value range across jump function JFUNC that is associated with
    edge CS with param of callee of PARAM_TYPE and update DEST_PLATS
    accordingly.  */
Index: ipa-fnsummary.c
===================================================================
--- ipa-fnsummary.c	(revision 278094)
+++ ipa-fnsummary.c	(working copy)
@@ -316,6 +316,7 @@  static void
 evaluate_conditions_for_known_args (struct cgraph_node *node,
 				    bool inline_p,
 				    vec<tree> known_vals,
+				    vec<value_range> known_value_ranges,
 				    vec<ipa_agg_jump_function_p>
 				    known_aggs,
 				    clause_t *ret_clause,
@@ -372,7 +373,9 @@  evaluate_conditions_for_known_args (stru
 	    val = NULL_TREE;
 	}
 
-      if (!val)
+      if (!val
+	  && (c->code == predicate::changed
+	      || c->code == predicate::is_not_constant))
 	{
 	  clause |= 1 << (i + predicate::first_dynamic_condition);
 	  nonspec_clause |= 1 << (i + predicate::first_dynamic_condition);
@@ -384,48 +387,101 @@  evaluate_conditions_for_known_args (stru
 	  continue;
 	}
 
-      if (TYPE_SIZE (c->type) != TYPE_SIZE (TREE_TYPE (val)))
-	{
-	  clause |= 1 << (i + predicate::first_dynamic_condition);
-	  nonspec_clause |= 1 << (i + predicate::first_dynamic_condition);
-	  continue;
-	}
       if (c->code == predicate::is_not_constant)
 	{
 	  nonspec_clause |= 1 << (i + predicate::first_dynamic_condition);
 	  continue;
 	}
 
-      val = fold_unary (VIEW_CONVERT_EXPR, c->type, val);
-      for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++)
+      if (val && TYPE_SIZE (c->type) == TYPE_SIZE (TREE_TYPE (val)))
 	{
-	  if (!val)
-	    break;
-	  if (!op->val[0])
-	    val = fold_unary (op->code, op->type, val);
-	  else if (!op->val[1])
-	    val = fold_binary (op->code, op->type,
-			       op->index ? op->val[0] : val,
-			       op->index ? val : op->val[0]);
-	  else if (op->index == 0)
-	    val = fold_ternary (op->code, op->type,
-				val, op->val[0], op->val[1]);
-	  else if (op->index == 1)
-	    val = fold_ternary (op->code, op->type,
-				op->val[0], val, op->val[1]);
-	  else if (op->index == 2)
-	    val = fold_ternary (op->code, op->type,
-				op->val[0], op->val[1], val);
-	  else
-	    val = NULL_TREE;
+	  if (c->type != TREE_TYPE (val))
+	    val = fold_unary (VIEW_CONVERT_EXPR, c->type, val);
+	  for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++)
+	    {
+	      if (!val)
+		break;
+	      if (!op->val[0])
+		val = fold_unary (op->code, op->type, val);
+	      else if (!op->val[1])
+		val = fold_binary (op->code, op->type,
+				   op->index ? op->val[0] : val,
+				   op->index ? val : op->val[0]);
+	      else if (op->index == 0)
+		val = fold_ternary (op->code, op->type,
+				    val, op->val[0], op->val[1]);
+	      else if (op->index == 1)
+		val = fold_ternary (op->code, op->type,
+				    op->val[0], val, op->val[1]);
+	      else if (op->index == 2)
+		val = fold_ternary (op->code, op->type,
+				    op->val[0], op->val[1], val);
+	      else
+		val = NULL_TREE;
+	    }
+
+	  res = val
+	    ? fold_binary_to_constant (c->code, boolean_type_node, val, c->val)
+	    : NULL;
+
+	  if (res && integer_zerop (res))
+	    continue;
+	  if (res && integer_onep (res))
+	    {
+	      clause |= 1 << (i + predicate::first_dynamic_condition);
+	      nonspec_clause |= 1 << (i + predicate::first_dynamic_condition);
+	      continue;
+	    }
 	}
+      if (c->operand_num < (int) known_value_ranges.length ()
+	  && !c->agg_contents
+	  && !known_value_ranges[c->operand_num].undefined_p ()
+	  && !known_value_ranges[c->operand_num].varying_p ()
+	  && TYPE_SIZE (c->type)
+		 == TYPE_SIZE (known_value_ranges[c->operand_num].type ())
+	  && (!val || TREE_CODE (val) != INTEGER_CST))
+	{
+	  value_range vr = known_value_ranges[c->operand_num];
+	  if (!useless_type_conversion_p (c->type, vr.type ()))
+	    {
+	      value_range res;
+	      range_fold_unary_expr (&res, NOP_EXPR,
+				     c->type, &vr, vr.type ());
+	      vr = res;
+	    }
+	  tree type = c->type;
 
-      res = val
-	? fold_binary_to_constant (c->code, boolean_type_node, val, c->val)
-	: NULL;
+	  for (j = 0; vec_safe_iterate (c->param_ops, j, &op); j++)
+	    {
+	      if (vr.varying_p () || vr.undefined_p ())
+		break;
 
-      if (res && integer_zerop (res))
-	continue;
+	      value_range res;
+	      if (!op->val[0])
+	        range_fold_unary_expr (&res, op->code, op->type, &vr, type);
+	      else if (!op->val[1])
+		{
+		  value_range op0 (VR_RANGE, op->val[0], op->val[0]);
+		  range_fold_binary_expr (&res, op->code, op->type,
+					  op->index ? &op0 : &vr,
+					  op->index ? &vr : &op0);
+		}
+	      else
+		gcc_unreachable ();
+	      type = op->type;
+	      vr = res;
+	    }
+	  if (!vr.varying_p () && !vr.undefined_p ())
+	    {
+	      value_range res;
+	      value_range val_vr (VR_RANGE, c->val, c->val);
+	      range_fold_binary_expr (&res, c->code, boolean_type_node,
+				      &vr,
+				      &val_vr);
+	      if (res.zero_p ())
+		continue;
+	    }
+	}
 
       clause |= 1 << (i + predicate::first_dynamic_condition);
       nonspec_clause |= 1 << (i + predicate::first_dynamic_condition);
@@ -450,6 +506,7 @@  evaluate_properties_for_edge (struct cgr
   struct cgraph_node *callee = e->callee->ultimate_alias_target ();
   class ipa_fn_summary *info = ipa_fn_summaries->get (callee);
   vec<tree> known_vals = vNULL;
+  auto_vec<value_range, 32> known_value_ranges;
   vec<ipa_agg_jump_function_p> known_aggs = vNULL;
   class ipa_edge_args *args;
 
@@ -477,6 +534,8 @@  evaluate_properties_for_edge (struct cgr
 
       if (count && (info->conds || known_vals_ptr))
 	known_vals.safe_grow_cleared (count);
+      if (count && info->conds)
+	known_value_ranges.safe_grow_cleared (count);
       if (count && (info->conds || known_aggs_ptr))
 	known_aggs.safe_grow_cleared (count);
       if (count && known_contexts_ptr)
@@ -505,14 +564,18 @@  evaluate_properties_for_edge (struct cgr
 	    else if (inline_p && !es->param[i].change_prob)
 	      known_vals[i] = error_mark_node;
 
-	    if (known_contexts_ptr)
-	      (*known_contexts_ptr)[i]
-		= ipa_context_from_jfunc (caller_parms_info, e, i, jf);
-	    /* TODO: When IPA-CP starts propagating and merging aggregate jump
-	       functions, use its knowledge of the caller too, just like the
-	       scalar case above.  */
-	    known_aggs[i] = &jf->agg;
-	  }
+            if (known_contexts_ptr)
+              (*known_contexts_ptr)[i]
+                = ipa_context_from_jfunc (caller_parms_info, e, i, jf);
+            /* TODO: When IPA-CP starts propagating and merging aggregate jump
+               functions, use its knowledge of the caller too, just like the
+               scalar case above.  */
+            known_aggs[i] = &jf->agg;
+            if (info->conds)
+              known_value_ranges[i] 
+                = ipa_value_range_from_jfunc (caller_parms_info, e, jf,
+                                              ipa_get_type (callee_pi, i));
+          }
 	else
 	  gcc_assert (callee->thunk.thunk_p);
     }
@@ -534,7 +597,9 @@  evaluate_properties_for_edge (struct cgr
     }
 
   evaluate_conditions_for_known_args (callee, inline_p,
-				      known_vals, known_aggs, clause_ptr,
+				      known_vals,
+				      known_value_ranges,
+				      known_aggs, clause_ptr,
 				      nonspec_clause_ptr);
 
   if (known_vals_ptr)
@@ -657,6 +722,7 @@  ipa_fn_summary_t::duplicate (cgraph_node
       evaluate_conditions_for_known_args (dst, false,
 					  known_vals,
 					  vNULL,
+					  vNULL,
 					  &possible_truths,
 					  /* We are going to specialize,
 					     so ignore nonspec truths.  */
@@ -3355,8 +3424,9 @@  estimate_ipcp_clone_size_and_time (struc
 {
   clause_t clause, nonspec_clause;
 
-  evaluate_conditions_for_known_args (node, false, known_vals, known_aggs,
-				      &clause, &nonspec_clause);
+  /* TODO: Also pass known value ranges.  */
+  evaluate_conditions_for_known_args (node, false, known_vals, vNULL,
+				      known_aggs, &clause, &nonspec_clause);
   ipa_call_context ctx (node, clause, nonspec_clause,
 		        known_vals, known_contexts,
 		        known_aggs, vNULL);
@@ -3576,7 +3646,8 @@  ipa_merge_fn_summary_after_inlining (str
   info->fp_expressions |= callee_info->fp_expressions;
 
   if (callee_info->conds)
-    evaluate_properties_for_edge (edge, true, &clause, NULL, NULL, NULL, NULL);
+    evaluate_properties_for_edge (edge, true, &clause,
+				  NULL, NULL, NULL, NULL);
   if (ipa_node_params_sum && callee_info->conds)
     {
       class ipa_edge_args *args = IPA_EDGE_REF (edge);
Index: ipa-prop.h
===================================================================
--- ipa-prop.h	(revision 278094)
+++ ipa-prop.h	(working copy)
@@ -918,6 +918,8 @@  ipa_polymorphic_call_context ipa_context
 						     cgraph_edge *,
 						     int,
 						     ipa_jump_func *);
+value_range ipa_value_range_from_jfunc (ipa_node_params *, cgraph_edge *,
+					ipa_jump_func *, tree);
 void ipa_dump_param (FILE *, class ipa_node_params *info, int i);
 void ipa_release_body_info (struct ipa_func_body_info *);
 tree ipa_get_callee_param_type (struct cgraph_edge *e, int i);
Index: testsuite/gcc.dg/ipa/inline-9.c
===================================================================
--- testsuite/gcc.dg/ipa/inline-9.c	(nonexistent)
+++ testsuite/gcc.dg/ipa/inline-9.c	(working copy)
@@ -0,0 +1,21 @@ 
+/* { dg-options "-Os -fdump-ipa-inline"  } */
+int test(int a)
+{
+ if (a>100)
+   {
+     foo();
+     foo();
+     foo();
+     foo();
+     foo();
+     foo();
+     foo();
+     foo();
+   }
+}
+main()
+{
+  for (int i=0;i<100;i++)
+    test(i);
+}
+/* { dg-final { scan-tree-dump "Inlined 1 calls" "inline" } } */