public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jakub Jelinek <jakub@redhat.com>
To: Jeff Law <law@redhat.com>
Cc: gcc-patches@gcc.gnu.org
Subject: [PATCH] Optimize divmod expansion (PR middle-end/79665)
Date: Wed, 22 Feb 2017 21:50:00 -0000	[thread overview]
Message-ID: <20170222214046.GA1849@tucnak> (raw)

Hi!

If both arguments of integer division or modulo are known to be non-negative
in corresponding signed type, then signed as well as unsigned division/modulo
shall have the exact same result and therefore we can choose between those
two depending on which one is faster (or shorter for -Os), which varries
a lot depending on target and especially for constant divisors on the exact
divisor.  expand_divmod itself is too complicated and we don't even have
the ability to ask about costs e.g. for highpart multiplication without
actually expanding it, so this patch just in that case tries both sequences,
computes their costs and uses the cheaper (and for equal cost honors the
actual original signedness of the operation).

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2017-02-22  Jakub Jelinek  <jakub@redhat.com>

	PR middle-end/79665
	* internal-fn.c (get_range_pos_neg): Moved to ...
	* tree.c (get_range_pos_neg): ... here.  No longer static.
	* tree.h (get_range_pos_neg): New prototype.
	* expr.c (expand_expr_real_2) <case TRUNC_DIV_EXPR>: If both arguments
	are known to be in between 0 and signed maximum inclusive, try to
	expand both unsigned and signed divmod and use the cheaper one from
	those.

--- gcc/internal-fn.c.jj	2017-02-11 09:14:56.000000000 +0100
+++ gcc/internal-fn.c	2017-02-22 10:09:03.503254909 +0100
@@ -413,86 +413,6 @@ expand_FALLTHROUGH (internal_fn, gcall *
 	    "invalid use of attribute %<fallthrough%>");
 }
 
-/* Helper function for expand_addsub_overflow.  Return 1
-   if ARG interpreted as signed in its precision is known to be always
-   positive or 2 if ARG is known to be always negative, or 3 if ARG may
-   be positive or negative.  */
-
-static int
-get_range_pos_neg (tree arg)
-{
-  if (arg == error_mark_node)
-    return 3;
-
-  int prec = TYPE_PRECISION (TREE_TYPE (arg));
-  int cnt = 0;
-  if (TREE_CODE (arg) == INTEGER_CST)
-    {
-      wide_int w = wi::sext (arg, prec);
-      if (wi::neg_p (w))
-	return 2;
-      else
-	return 1;
-    }
-  while (CONVERT_EXPR_P (arg)
-	 && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (arg, 0)))
-	 && TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg, 0))) <= prec)
-    {
-      arg = TREE_OPERAND (arg, 0);
-      /* Narrower value zero extended into wider type
-	 will always result in positive values.  */
-      if (TYPE_UNSIGNED (TREE_TYPE (arg))
-	  && TYPE_PRECISION (TREE_TYPE (arg)) < prec)
-	return 1;
-      prec = TYPE_PRECISION (TREE_TYPE (arg));
-      if (++cnt > 30)
-	return 3;
-    }
-
-  if (TREE_CODE (arg) != SSA_NAME)
-    return 3;
-  wide_int arg_min, arg_max;
-  while (get_range_info (arg, &arg_min, &arg_max) != VR_RANGE)
-    {
-      gimple *g = SSA_NAME_DEF_STMT (arg);
-      if (is_gimple_assign (g)
-	  && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (g)))
-	{
-	  tree t = gimple_assign_rhs1 (g);
-	  if (INTEGRAL_TYPE_P (TREE_TYPE (t))
-	      && TYPE_PRECISION (TREE_TYPE (t)) <= prec)
-	    {
-	      if (TYPE_UNSIGNED (TREE_TYPE (t))
-		  && TYPE_PRECISION (TREE_TYPE (t)) < prec)
-		return 1;
-	      prec = TYPE_PRECISION (TREE_TYPE (t));
-	      arg = t;
-	      if (++cnt > 30)
-		return 3;
-	      continue;
-	    }
-	}
-      return 3;
-    }
-  if (TYPE_UNSIGNED (TREE_TYPE (arg)))
-    {
-      /* For unsigned values, the "positive" range comes
-	 below the "negative" range.  */
-      if (!wi::neg_p (wi::sext (arg_max, prec), SIGNED))
-	return 1;
-      if (wi::neg_p (wi::sext (arg_min, prec), SIGNED))
-	return 2;
-    }
-  else
-    {
-      if (!wi::neg_p (wi::sext (arg_min, prec), SIGNED))
-	return 1;
-      if (wi::neg_p (wi::sext (arg_max, prec), SIGNED))
-	return 2;
-    }
-  return 3;
-}
-
 /* Return minimum precision needed to represent all values
    of ARG in SIGNed integral type.  */
 
--- gcc/tree.c.jj	2017-02-09 14:55:34.000000000 +0100
+++ gcc/tree.c	2017-02-22 10:10:36.525062066 +0100
@@ -14205,6 +14205,88 @@ verify_type (const_tree t)
 }
 
 
+/* Return 1 if ARG interpreted as signed in its precision is known to be
+   always positive or 2 if ARG is known to be always negative, or 3 if
+   ARG may be positive or negative.  */
+
+int
+get_range_pos_neg (tree arg)
+{
+  if (arg == error_mark_node)
+    return 3;
+
+  int prec = TYPE_PRECISION (TREE_TYPE (arg));
+  int cnt = 0;
+  if (TREE_CODE (arg) == INTEGER_CST)
+    {
+      wide_int w = wi::sext (arg, prec);
+      if (wi::neg_p (w))
+	return 2;
+      else
+	return 1;
+    }
+  while (CONVERT_EXPR_P (arg)
+	 && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (arg, 0)))
+	 && TYPE_PRECISION (TREE_TYPE (TREE_OPERAND (arg, 0))) <= prec)
+    {
+      arg = TREE_OPERAND (arg, 0);
+      /* Narrower value zero extended into wider type
+	 will always result in positive values.  */
+      if (TYPE_UNSIGNED (TREE_TYPE (arg))
+	  && TYPE_PRECISION (TREE_TYPE (arg)) < prec)
+	return 1;
+      prec = TYPE_PRECISION (TREE_TYPE (arg));
+      if (++cnt > 30)
+	return 3;
+    }
+
+  if (TREE_CODE (arg) != SSA_NAME)
+    return 3;
+  wide_int arg_min, arg_max;
+  while (get_range_info (arg, &arg_min, &arg_max) != VR_RANGE)
+    {
+      gimple *g = SSA_NAME_DEF_STMT (arg);
+      if (is_gimple_assign (g)
+	  && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (g)))
+	{
+	  tree t = gimple_assign_rhs1 (g);
+	  if (INTEGRAL_TYPE_P (TREE_TYPE (t))
+	      && TYPE_PRECISION (TREE_TYPE (t)) <= prec)
+	    {
+	      if (TYPE_UNSIGNED (TREE_TYPE (t))
+		  && TYPE_PRECISION (TREE_TYPE (t)) < prec)
+		return 1;
+	      prec = TYPE_PRECISION (TREE_TYPE (t));
+	      arg = t;
+	      if (++cnt > 30)
+		return 3;
+	      continue;
+	    }
+	}
+      return 3;
+    }
+  if (TYPE_UNSIGNED (TREE_TYPE (arg)))
+    {
+      /* For unsigned values, the "positive" range comes
+	 below the "negative" range.  */
+      if (!wi::neg_p (wi::sext (arg_max, prec), SIGNED))
+	return 1;
+      if (wi::neg_p (wi::sext (arg_min, prec), SIGNED))
+	return 2;
+    }
+  else
+    {
+      if (!wi::neg_p (wi::sext (arg_min, prec), SIGNED))
+	return 1;
+      if (wi::neg_p (wi::sext (arg_max, prec), SIGNED))
+	return 2;
+    }
+  return 3;
+}
+
+
+
+
 /* Return true if ARG is marked with the nonnull attribute in the
    current function signature.  */
 
--- gcc/tree.h.jj	2017-02-09 14:55:34.000000000 +0100
+++ gcc/tree.h	2017-02-22 10:11:05.448691171 +0100
@@ -4876,6 +4876,7 @@ extern bool gimple_canonical_types_compa
 						 bool trust_type_canonical = true);
 extern bool type_with_interoperable_signedness (const_tree);
 extern bitmap get_nonnull_args (const_tree);
+extern int get_range_pos_neg (tree);
 
 /* Return simplified tree code of type that is used for canonical type
    merging.  */
--- gcc/expr.c.jj	2017-01-19 16:58:23.000000000 +0100
+++ gcc/expr.c	2017-02-22 10:22:20.290996636 +0100
@@ -8809,6 +8809,34 @@ expand_expr_real_2 (sepops ops, rtx targ
 	 where some terms of the dividend have coeffs divisible by it.  */
       expand_operands (treeop0, treeop1,
 		       subtarget, &op0, &op1, EXPAND_NORMAL);
+      if (SCALAR_INT_MODE_P (mode)
+	  && optimize >= 2
+	  && get_range_pos_neg (treeop0) == 1
+	  && get_range_pos_neg (treeop1) == 1)
+	{
+	  /* If both arguments are known to be positive when interpreted
+	     as signed, we can expand it as both signed and unsigned
+	     division or modulo.  Choose the cheaper sequence in that case.  */
+	  bool speed_p = optimize_insn_for_speed_p ();
+	  do_pending_stack_adjust ();
+	  start_sequence ();
+	  rtx uns_ret = expand_divmod (0, code, mode, op0, op1, target, 1);
+	  rtx_insn *uns_insns = get_insns ();
+	  end_sequence ();
+	  start_sequence ();
+	  rtx sgn_ret = expand_divmod (0, code, mode, op0, op1, target, 0);
+	  rtx_insn *sgn_insns = get_insns ();
+	  end_sequence ();
+	  unsigned uns_cost = seq_cost (uns_insns, speed_p);
+	  unsigned sgn_cost = seq_cost (sgn_insns, speed_p);
+	  if (uns_cost < sgn_cost || (uns_cost == sgn_cost && unsignedp))
+	    {
+	      emit_insn (uns_insns);
+	      return uns_ret;
+	    }
+	  emit_insn (sgn_insns);
+	  return sgn_ret;
+	}
       return expand_divmod (0, code, mode, op0, op1, target, unsignedp);
 
     case RDIV_EXPR:

	Jakub

             reply	other threads:[~2017-02-22 21:41 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-02-22 21:50 Jakub Jelinek [this message]
2017-02-23  6:00 ` Jeff Law
2017-05-31  8:15   ` Georg-Johann Lay
2017-05-31  8:19     ` Jakub Jelinek
2017-05-31  9:00       ` Georg-Johann Lay
2017-05-31  9:08         ` Jakub Jelinek
2017-05-31 14:04           ` Georg-Johann Lay

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170222214046.GA1849@tucnak \
    --to=jakub@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=law@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).