This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Gimplify builtin_expect on short-circuiting conditionals


The gimplifier treats __builtin_expect as a regular function call.
This does not seem to work well if the argument is a logical
expression with short-circuit semantics.

Consider this testcase:

  f (int i, float j)  
  {  
    if (__builtin_expect (i > 0 && j, 0))  
      g (); 
  }

The conditional as an argument is gimplified into a conditional
expression without the builtin_expect:

  <bb 2>:
    if (i_2(D) <= 0)
      goto <bb 4>;
    else
      goto <bb 3>;
  
  <bb 3>:
    D.1583_4 = j_3(D) != 0.0;
    if (D.1583_4 == 0)
      goto <bb 4>;
    else
      goto <bb 5>;
  
  <bb 4>:
  
  <bb 5>:
    # iftmp.0_1 = PHI <1(3), 0(4)>
    D.1585_8 = (long int) iftmp.0_1;
    D.1586_9 = __builtin_expect (D.1585_8, 0);
    if (D.1586_9 != 0)
      goto <bb 6>;
    else
      goto <bb 7>;
  
  <bb 6>:
    g ();
  
  <bb 7>:
    return;

Then block 5 is basically eliminated together with the effect of the
builtin_expect.

GCC 3.4 used to take account of the builtin_expect in
expand_builtin_expect_jump.  It generated the sequence with do_jump.
Then predicted each jump insn as taken or not-taken depending on which
label it was jumping too.

This patch distributes the builtin_expect call across the operands of
TRUTH_ANDIF_EXPR and TRUTH_ORIF_EXPR in gimplify_cond_expr as we
recurse in shortcut_cond_expr and shortcut_cond_r.  Note that I don't
ever need to invert the prediction because the condition is never
inverted only the arms of the if statement are exchanged.

I tried to make the tests target-independent by using the float
comparison as the second operand.  This should prevent
LOGICAL_OP_NON_SHORT_CIRCUIT targets to turn the operators into their
non-short-circuiting counterparts.

The fifth testcase verifies that if there is a builtin_expect on an
operand it takes precedence over the one on the whole expression.

Regression tested on mipsisa64-elf, bootstrapped and regression tested
on x86_64-linux.

OK?

Adam

	* gimplify.c (remove_expected_value, add_expected_value): New
	functions.
	(shortcut_cond_expr, shortcut_cond_r): Use them to distribute
	builtin_expect across operands.  Add new argument expected.
	(split_cond_expr_p): New function.
	(gimplify_cond_expr): Use it to see through builtin_expects
	when looking for short-circuiting operators.


	* gcc.dg/tree-ssa/builtin-expect-1.c: New test.
	* gcc.dg/tree-ssa/builtin-expect-2.c: New test.
	* gcc.dg/tree-ssa/builtin-expect-3.c: New test.
	* gcc.dg/tree-ssa/builtin-expect-4.c: New test.
	* gcc.dg/tree-ssa/builtin-expect-5.c: New test.

Index: gimplify.c
===================================================================
--- gimplify.c	(revision 125932)
+++ gimplify.c	(working copy)
@@ -2150,23 +2150,134 @@ gimplify_call_expr (tree *expr_p, tree *
   return ret;
 }
 
+/* If PRED is a predicate see if there is a builtin_expect call at the
+   top level and return the real predicate inside the builtin_expect.
+   In this case also return the expected value in *EXPECTED.
+   Otherwise, return the the original expression and don't change the
+   value in *EXPECTED.  */
+
+static tree
+remove_expected_value (tree pred, tree *expected)
+{
+  tree fndecl, arg0, arg1, orig = pred;
+  bool invert_pred = false;
+
+  if ((TREE_CODE (pred) == NE_EXPR
+       || TREE_CODE (pred) == EQ_EXPR))
+    {
+      tree rhs = TREE_OPERAND (pred, 1);
+
+      if (integer_onep (rhs))
+	invert_pred = TREE_CODE (pred) == NE_EXPR;
+      else if (integer_zerop (rhs))
+	invert_pred = TREE_CODE (pred) == EQ_EXPR;
+      else
+	return orig;
+
+      pred = TREE_OPERAND (pred, 0);
+    }
+
+  if (TREE_CODE (pred) != CALL_EXPR)
+    return orig;
+
+  fndecl = get_callee_fndecl (pred);
+  if (!fndecl
+      || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL
+      || DECL_FUNCTION_CODE (fndecl) != BUILT_IN_EXPECT
+      || call_expr_nargs (pred) != 2)
+    return orig;
+
+  arg0 = CALL_EXPR_ARG (pred, 0);
+  arg1 = CALL_EXPR_ARG (pred, 1);
+
+  /* See through the cast from truthvalue_type_node to long.  */
+  while (TREE_CODE (arg0) == NOP_EXPR
+	 && INTEGRAL_TYPE_P (TREE_TYPE (arg0))
+	 && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (arg0, 0))))
+    arg0 = TREE_OPERAND (arg0, 0);
+
+  if (TREE_CODE (TREE_TYPE (arg1)) != INTEGER_TYPE
+      || (!integer_zerop (arg1) && !integer_onep (arg1)))
+    return orig;
+
+  *expected = arg1;
+
+  arg0 = gimple_boolify (arg0);
+  if (invert_pred)
+    return invert_truthvalue (arg0);
+  else
+    return arg0;
+}
+
+/* Return true if COND_EXPR should be split using
+   shortcut_cond_expr.  */
+
+static bool
+split_cond_expr_p (tree cond_expr)
+{
+  tree pred, expected;
+
+  gcc_assert (TREE_CODE (cond_expr) == COND_EXPR);
+  pred = TREE_OPERAND (cond_expr, 0);
+
+  pred = remove_expected_value (pred, &expected);
+
+  return (TREE_CODE (pred) == TRUTH_ANDIF_EXPR
+	  || TREE_CODE (pred) == TRUTH_ORIF_EXPR);
+}
+
+/* Return a call to builtin_expect with PRED and EXPECTED as its
+   arguments.  Return the original expression PRED if EXPECTED is a
+   NULL_TREE.  */
+
+static tree
+add_expected_value (tree pred, tree expected)
+{
+  tree fn, arglist, arg_types, pred_type, expected_type, call_expr, ret_type;
+
+  if (expected == NULL_TREE)
+    return pred;
+
+  fn = built_in_decls[BUILT_IN_EXPECT];
+  arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn));
+  ret_type = TREE_TYPE (TREE_TYPE (fn));
+  pred_type = TREE_VALUE (arg_types);
+  expected_type = TREE_VALUE (TREE_CHAIN (arg_types));
+
+  pred = fold_convert (pred_type, pred);
+  expected = convert (expected_type, expected);
+
+  arglist = build_tree_list (NULL_TREE, expected);
+  arglist = tree_cons (NULL_TREE, pred, arglist);
+  call_expr = build_function_call_expr (fn, arglist);
+
+  return build2 (NE_EXPR, TREE_TYPE (pred), call_expr,
+		 convert (ret_type, integer_zero_node));
+}
+
 /* Handle shortcut semantics in the predicate operand of a COND_EXPR by
    rewriting it into multiple COND_EXPRs, and possibly GOTO_EXPRs.
 
    TRUE_LABEL_P and FALSE_LABEL_P point to the labels to jump to if the
    condition is true or false, respectively.  If null, we should generate
-   our own to skip over the evaluation of this specific expression.
+   our own to skip over the evaluation of this specific expression.  If
+   EXPECTED is set then place a builtin_expect around each predicate with
+   this value.  Don't do this if a more local builtin_expect is found.
 
    This function is the tree equivalent of do_jump.
 
    shortcut_cond_r should only be called by shortcut_cond_expr.  */
 
 static tree
-shortcut_cond_r (tree pred, tree *true_label_p, tree *false_label_p)
+shortcut_cond_r (tree pred, tree *true_label_p, tree *false_label_p,
+		 tree expected)
 {
   tree local_label = NULL_TREE;
   tree t, expr = NULL;
 
+  /* Find the real predicate if this is within a builtin_expect.  */
+  pred = remove_expected_value (pred, &expected);
+
   /* OK, it's not a simple case; we need to pull apart the COND_EXPR to
      retain the shortcut semantics.  Just insert the gotos here;
      shortcut_cond_expr will append the real blocks later.  */
@@ -2181,11 +2292,12 @@ shortcut_cond_r (tree pred, tree *true_l
       if (false_label_p == NULL)
 	false_label_p = &local_label;
 
-      t = shortcut_cond_r (TREE_OPERAND (pred, 0), NULL, false_label_p);
+      t = shortcut_cond_r (TREE_OPERAND (pred, 0), NULL, false_label_p,
+			   expected);
       append_to_statement_list (t, &expr);
 
-      t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p,
-			   false_label_p);
+      t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p, false_label_p,
+			   expected);
       append_to_statement_list (t, &expr);
     }
   else if (TREE_CODE (pred) == TRUTH_ORIF_EXPR)
@@ -2199,11 +2311,12 @@ shortcut_cond_r (tree pred, tree *true_l
       if (true_label_p == NULL)
 	true_label_p = &local_label;
 
-      t = shortcut_cond_r (TREE_OPERAND (pred, 0), true_label_p, NULL);
+      t = shortcut_cond_r (TREE_OPERAND (pred, 0), true_label_p, NULL,
+			   expected);
       append_to_statement_list (t, &expr);
 
       t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p,
-			   false_label_p);
+			   false_label_p, expected);
       append_to_statement_list (t, &expr);
     }
   else if (TREE_CODE (pred) == COND_EXPR)
@@ -2213,15 +2326,17 @@ shortcut_cond_r (tree pred, tree *true_l
 	   if (b) goto yes; else goto no;
 	 else
 	   if (c) goto yes; else goto no;  */
-      expr = build3 (COND_EXPR, void_type_node, TREE_OPERAND (pred, 0),
+      expr = build3 (COND_EXPR, void_type_node,
+		     add_expected_value (TREE_OPERAND (pred, 0), expected),
 		     shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p,
-				      false_label_p),
+				      false_label_p, expected),
 		     shortcut_cond_r (TREE_OPERAND (pred, 2), true_label_p,
-				      false_label_p));
+				      false_label_p, expected));
     }
   else
     {
-      expr = build3 (COND_EXPR, void_type_node, pred,
+      expr = build3 (COND_EXPR, void_type_node,
+		     add_expected_value (pred, expected),
 		     build_and_jump (true_label_p),
 		     build_and_jump (false_label_p));
     }
@@ -2235,8 +2350,14 @@ shortcut_cond_r (tree pred, tree *true_l
   return expr;
 }
 
+/* EXPR is a COND_EXPR with a predicate containing TRUTH_ORIF_EXPR or
+   TRUTH_ANDIF_EXPR.  Split the COND_EXPR with shortcutting the
+   individual terms.  If EXPECTED is set then place a builtin_expect
+   around each predicate with this value.  Do not do this if a more
+   local builtin_expect is found.  */
+
 static tree
-shortcut_cond_expr (tree expr)
+shortcut_cond_expr (tree expr, tree expected)
 {
   tree pred = TREE_OPERAND (expr, 0);
   tree then_ = TREE_OPERAND (expr, 1);
@@ -2248,6 +2369,9 @@ shortcut_cond_expr (tree expr)
   bool then_se = then_ && TREE_SIDE_EFFECTS (then_);
   bool else_se = else_ && TREE_SIDE_EFFECTS (else_);
 
+  /* Find the real predicate if this is within a builtin_expect.  */
+  pred = remove_expected_value (pred, &expected);
+
   /* First do simple transformations.  */
   if (!else_se)
     {
@@ -2255,7 +2379,7 @@ shortcut_cond_expr (tree expr)
       while (TREE_CODE (pred) == TRUTH_ANDIF_EXPR)
 	{
 	  TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1);
-	  then_ = shortcut_cond_expr (expr);
+	  then_ = shortcut_cond_expr (expr, expected);
 	  then_se = then_ && TREE_SIDE_EFFECTS (then_);
 	  pred = TREE_OPERAND (pred, 0);
 	  expr = build3 (COND_EXPR, void_type_node, pred, then_, NULL_TREE);
@@ -2270,7 +2394,7 @@ shortcut_cond_expr (tree expr)
       while (TREE_CODE (pred) == TRUTH_ORIF_EXPR)
 	{
 	  TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1);
-	  else_ = shortcut_cond_expr (expr);
+	  else_ = shortcut_cond_expr (expr, expected);
 	  else_se = else_ && TREE_SIDE_EFFECTS (else_);
 	  pred = TREE_OPERAND (pred, 0);
 	  expr = build3 (COND_EXPR, void_type_node, pred, NULL_TREE, else_);
@@ -2280,7 +2404,10 @@ shortcut_cond_expr (tree expr)
   /* If we're done, great.  */
   if (TREE_CODE (pred) != TRUTH_ANDIF_EXPR
       && TREE_CODE (pred) != TRUTH_ORIF_EXPR)
-    return expr;
+    {
+      TREE_OPERAND (expr, 0) = add_expected_value (pred, expected);
+      return expr;
+    }
 
   /* Otherwise we need to mess with gotos.  Change
        if (a) c; else d;
@@ -2327,7 +2454,7 @@ shortcut_cond_expr (tree expr)
 
   /* If there was nothing else in our arms, just forward the label(s).  */
   if (!then_se && !else_se)
-    return shortcut_cond_r (pred, true_label_p, false_label_p);
+    return shortcut_cond_r (pred, true_label_p, false_label_p, expected);
 
   /* If our last subexpression already has a terminal label, reuse it.  */
   if (else_se)
@@ -2358,7 +2485,7 @@ shortcut_cond_expr (tree expr)
      non-void function.  */
   jump_over_else = block_may_fallthru (then_);
 
-  pred = shortcut_cond_r (pred, true_label_p, false_label_p);
+  pred = shortcut_cond_r (pred, true_label_p, false_label_p, expected);
 
   expr = NULL;
   append_to_statement_list (pred, &expr);
@@ -2507,10 +2634,9 @@ gimplify_cond_expr (tree *expr_p, tree *
   TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr, 0));
 
   /* Break apart && and || conditions.  */
-  if (TREE_CODE (TREE_OPERAND (expr, 0)) == TRUTH_ANDIF_EXPR
-      || TREE_CODE (TREE_OPERAND (expr, 0)) == TRUTH_ORIF_EXPR)
+  if (split_cond_expr_p (expr))
     {
-      expr = shortcut_cond_expr (expr);
+      expr = shortcut_cond_expr (expr, NULL_TREE);
 
       if (expr != *expr_p)
 	{
Index: testsuite/gcc.dg/tree-ssa/builtin-expect-1.c
===================================================================
--- testsuite/gcc.dg/tree-ssa/builtin-expect-1.c	(revision 0)
+++ testsuite/gcc.dg/tree-ssa/builtin-expect-1.c	(revision 0)
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-gimple" } */
+
+f (int i, float j) 
+{ 
+  if (__builtin_expect (i > 0 && j, 0)) 
+    g ();
+} 
+
+/* { dg-final { scan-tree-dump-times {builtin_expect[^\n]*, 0\);\n[^\n]*if} 2 "gimple"} } */
+/* { dg-final { cleanup-tree-dump "gimple" } } */
Index: testsuite/gcc.dg/tree-ssa/builtin-expect-2.c
===================================================================
--- testsuite/gcc.dg/tree-ssa/builtin-expect-2.c	(revision 0)
+++ testsuite/gcc.dg/tree-ssa/builtin-expect-2.c	(revision 0)
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-gimple" } */
+
+f (int i, float j) 
+{ 
+  if (__builtin_expect (i > 0 || j, 0))
+    ;
+  else
+    g ();
+} 
+
+/* { dg-final { scan-tree-dump-times {builtin_expect[^\n]*, 0\);\n[^\n]*if} 2 "gimple"} } */
+/* { dg-final { cleanup-tree-dump "gimple" } } */
Index: testsuite/gcc.dg/tree-ssa/builtin-expect-3.c
===================================================================
--- testsuite/gcc.dg/tree-ssa/builtin-expect-3.c	(revision 0)
+++ testsuite/gcc.dg/tree-ssa/builtin-expect-3.c	(revision 0)
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-gimple" } */
+
+f (int i, float j) 
+{ 
+  if (__builtin_expect (i > 0 && j, 0))
+    a ();
+  else
+    b ();
+} 
+
+/* { dg-final { scan-tree-dump-times {builtin_expect[^\n]*, 0\);\n[^\n]*if} 2 "gimple"} } */
+/* { dg-final { cleanup-tree-dump "gimple" } } */
Index: testsuite/gcc.dg/tree-ssa/builtin-expect-4.c
===================================================================
--- testsuite/gcc.dg/tree-ssa/builtin-expect-4.c	(revision 0)
+++ testsuite/gcc.dg/tree-ssa/builtin-expect-4.c	(revision 0)
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-gimple" } */
+
+f (int i, float j) 
+{ 
+  if (__builtin_expect (i > 0 || j, 0))
+    a ();
+  else
+    b ();
+} 
+
+/* { dg-final { scan-tree-dump-times {builtin_expect[^\n]*, 0\);\n[^\n]*if} 2 "gimple"} } */
+/* { dg-final { cleanup-tree-dump "gimple" } } */
Index: testsuite/gcc.dg/tree-ssa/builtin-expect-5.c
===================================================================
--- testsuite/gcc.dg/tree-ssa/builtin-expect-5.c	(revision 0)
+++ testsuite/gcc.dg/tree-ssa/builtin-expect-5.c	(revision 0)
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-gimple" } */
+
+f (int i, float j) 
+{ 
+  if (__builtin_expect (i > 0 && __builtin_expect (j != 0, 1), 0))
+    a ();
+  else
+    b ();
+} 
+
+/* { dg-final { scan-tree-dump-times { if } 2 "gimple"} } */
+/* { dg-final { scan-tree-dump {builtin_expect[^\n]*, 0\);\n[^\n]*if} "gimple"} } */
+/* { dg-final { scan-tree-dump {builtin_expect[^\n]*, 1\);\n[^\n]*if} "gimple"} } */
+/* { dg-final { cleanup-tree-dump "gimple" } } */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]