public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jakub Jelinek <jakub@redhat.com>
To: Tamar Christina <Tamar.Christina@arm.com>
Cc: Jonathan Wakely <jwakely@redhat.com>,
	Richard Biener <rguenther@suse.de>,
	 "gcc-patches@gcc.gnu.org" <gcc-patches@gcc.gnu.org>,
	nd <nd@arm.com>
Subject: Re: [PATCH] middle-end: fix de-optimizations with bitclear patterns on signed values
Date: Thu, 4 Nov 2021 16:10:33 +0100	[thread overview]
Message-ID: <20211104151033.GY304296@tucnak> (raw)
In-Reply-To: <VI1PR08MB532597A8E6BCBFAC5285CE26FF8D9@VI1PR08MB5325.eurprd08.prod.outlook.com>

On Thu, Nov 04, 2021 at 12:19:34PM +0000, Tamar Christina wrote:
> I'm not sure the precision matters since if the conversion resulted in not enough
> precision such that It influences the compare it would have been optimized out.

You can't really rely on other optimizations being performed.  They will
usually happen, but might not because such code only materialized short time
ago without folding happening in between, or some debug counters or -fno-*
disabling some passes, ...

> --- a/gcc/tree-ssa-phiopt.c
> +++ b/gcc/tree-ssa-phiopt.c
> @@ -2038,6 +2038,34 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
>    gimple *orig_use_stmt = use_stmt;
>    tree orig_use_lhs = NULL_TREE;
>    int prec = TYPE_PRECISION (TREE_TYPE (phires));
> +  bool is_cast = false;
> +
> +  /* Deal with the case when match.pd has rewritten the (res & ~1) == 0
> +     into res <= 1 and has left a type-cast for signed types.  */
> +  if (gimple_assign_cast_p (use_stmt))
> +    {
> +      orig_use_lhs = gimple_assign_lhs (use_stmt);
> +      /* match.pd would have only done this for a signed type,
> +	 so the conversion must be to an unsigned one.  */
> +      tree ty1 = TREE_TYPE (gimple_assign_rhs1 (use_stmt));
> +      tree ty2 = TREE_TYPE (orig_use_lhs);

gimple_assign_rhs1 (use_stmt) is I think guaranteed to be phires
here.  And that has some of this checked already at the start of
the function:
  if (!INTEGRAL_TYPE_P (TREE_TYPE (phires))
      || TYPE_UNSIGNED (TREE_TYPE (phires))

> +
> +      if (TYPE_UNSIGNED (ty1) || !INTEGRAL_TYPE_P (ty1))
> +	return false;

So I think the above two lines are redundant.

> +      if (!TYPE_UNSIGNED (ty2) || !INTEGRAL_TYPE_P (ty2))
> +	return false;
> +      if (TYPE_PRECISION (ty1) != TYPE_PRECISION (ty2))
> +	return false;
> +      if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (orig_use_lhs))
> +	return false;
> +      if (EDGE_COUNT (phi_bb->preds) != 4)
> +	return false;
> +      if (!single_imm_use (orig_use_lhs, &use_p, &use_stmt))
> +	return false;
> +
> +      is_cast = true;
> +    }
> +
>    if (is_gimple_assign (use_stmt)

I'd feel much safer if this was else if rather than if.
The reason for the patch is that (res & ~1) == 0 is optimized
into (unsigned) res <= 1, right, so it can be either this or that
and you don't need both.  If you want to also handle both, that would
mean figuring all the details even for that case, handling of debug stmts
etc.

>        && gimple_assign_rhs_code (use_stmt) == BIT_AND_EXPR
>        && TREE_CODE (gimple_assign_rhs2 (use_stmt)) == INTEGER_CST
> @@ -2099,7 +2127,7 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
>        || !tree_fits_shwi_p (rhs)
>        || !IN_RANGE (tree_to_shwi (rhs), -1, 1))
>      return false;
> -  if (orig_use_lhs)
> +  if (orig_use_lhs && !is_cast)

Because otherwise it is unclear what the above means, the
intent is that the if handles the case where BIT_AND_EXPR is present,
but with both cast to unsigned and BIT_AND_EXPR present it acts differently.

> @@ -2345,6 +2373,8 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
>  	res_cmp = one_cmp == LT_EXPR ? GE_EXPR : LE_EXPR;
>        else if (integer_minus_onep (rhs))
>  	res_cmp = one_cmp == LT_EXPR ? GT_EXPR : LT_EXPR;
> +      else if (integer_onep (rhs) && is_cast)
> +	res_cmp = GE_EXPR;
>        else
>  	return false;
>        break;
> @@ -2353,6 +2383,8 @@ spaceship_replacement (basic_block cond_bb, basic_block middle_bb,
>  	res_cmp = one_cmp == LT_EXPR ? LE_EXPR : GE_EXPR;
>        else if (integer_zerop (rhs))
>  	res_cmp = one_cmp;
> +      else if (integer_onep (rhs) && is_cast)
> +	res_cmp = LE_EXPR;
>        else
>  	return false;
>        break;

I'm afraid this is still wrong.  Because is_cast which implies
that the comparison is done in unsigned type rather than signed type
which is otherwise ensured changes everything the code assumes.
While maybe EQ_EXPR and NE_EXPR will work the same whether it is unsigned or
signed comparison, the other comparisons certainly will not.

So, my preference would be instead of doing these 2 hunks handle the is_cast
case early, right before if (orig_use_lhs) above.  Something like:
  if (is_cast)
    {
      if (TREE_CODE (rhs) != INTEGER_CST)
	return false;
      /* As for -ffast-math we assume the 2 return to be
 	 impossible, canonicalize (unsigned) res <= 1U or
	 (unsigned) res < 2U into res >= 0 and (unsigned) res > 1U
	 or (unsigned) res >= 2U as res < 0.  */
      switch (cmp)
	{
	case LE_EXPR:
	  if (!integer_onep (rhs))
	    return false;
	  cmp = GE_EXPR;
	  break;
	case LT_EXPR:
	  if (wi::ne_p (wi::to_widest (rhs), 2))
	    return false;
	  cmp = GE_EXPR;
	  break;
	case GT_EXPR:
	  if (!integer_onep (rhs))
	    return false;
	  cmp = LT_EXPR;
	  break;
	case GE_EXPR:
	  if (wi::ne_p (wi::to_widest (rhs), 2))
	    return false;
	  cmp = LT_EXPR;
	  break;
	default:
	  return false;
	}
      rhs = build_zero_cst (TREE_TYPE (phires));
    }
  else if (orig_use_lhs)
    {
...
Similarly to the BIT_AND_EXPR case the above transforms
the is_cast case into a signed comparison that the later code
knows how to handle.

There is another problem though.  If there are debug stmts, the
code later on does:
          /* If there are debug uses, emit something like:
             # DEBUG D#1 => i_2(D) > j_3(D) ? 1 : -1
             # DEBUG D#2 => i_2(D) == j_3(D) ? 0 : D#1
             where > stands for the comparison that yielded 1
             and replace debug uses of phi result with that D#2.
             Ignore the value of 2, because if NaNs aren't expected,
             all floating point numbers should be comparable.  */
...
          replace_uses_by (phires, temp2);
          if (orig_use_lhs)
            replace_uses_by (orig_use_lhs, temp2);
For the is_cast case this creates invalid IL, because the uses of
orig_use_lhs if any expect an unsigned value, but temp2 is signed.
Perhaps when one uses <compare> stuff this will never happen, but
there is nothing that prevents a user to write his own code so that
spaceship_replacement actually triggers on it (i.e. results in the
same IL) and has debug info uses on it.  And we can't e.g. punt if
there are debug stmts and not otherwise because that would result
in -fcompare-debug differences.
So, I think we need:
       bool has_debug_uses = false;
+      bool has_cast_debug_uses = false;
...
-          if (!has_debug_uses)
+          if (!has_debug_uses || is_cast)
             FOR_EACH_IMM_USE_FAST (use_p, iter, orig_use_lhs)
               {
                 gimple *use_stmt = USE_STMT (use_p);
                 gcc_assert (is_gimple_debug (use_stmt));
                 has_debug_uses = true;
+                if (is_cast)
+                  has_cast_debug_uses = true;
              }
and later if (has_cast_debug_uses) create another debug temporary
temp3 with TREE_TYPE set to TREE_TYPE (orig_use_lhs),
the expression (that_uns_type) temp2 and use that temp3 instead of
temp2 in replace_uses_by (orig_use_lhs, temp2);

	Jakub


  reply	other threads:[~2021-11-04 15:10 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-10-15 11:08 Tamar Christina
2021-10-15 11:30 ` Richard Biener
2021-10-25 14:26   ` Tamar Christina
2021-10-26  8:26     ` Richard Biener
2021-10-26  8:35       ` Tamar Christina
2021-10-26  8:45         ` Richard Biener
2021-10-26 12:10           ` Tamar Christina
2021-10-26 13:13             ` Richard Biener
2021-10-26 13:20               ` Jakub Jelinek
2021-10-26 13:21                 ` Richard Biener
2021-10-26 13:36                   ` Jakub Jelinek
2021-10-26 13:38                     ` Richard Biener
2021-10-26 19:35                     ` Jonathan Wakely
2021-10-26 19:39                       ` Jakub Jelinek
2021-10-26 19:50                         ` Jonathan Wakely
2021-11-03 10:56                           ` Tamar Christina
2021-11-03 14:20                             ` Jakub Jelinek
2021-11-04 12:19                               ` Tamar Christina
2021-11-04 15:10                                 ` Jakub Jelinek [this message]
2021-11-12  7:30                                   ` Tamar Christina
2021-11-19  8:52                                     ` Tamar Christina
2021-11-19 11:19                                     ` Jakub Jelinek

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=20211104151033.GY304296@tucnak \
    --to=jakub@redhat.com \
    --cc=Tamar.Christina@arm.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jwakely@redhat.com \
    --cc=nd@arm.com \
    --cc=rguenther@suse.de \
    /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).