Bug 90879 - fold zero-equality of strcmp between a longer string and a smaller array
Summary: fold zero-equality of strcmp between a longer string and a smaller array
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 9.0
: P3 normal
Target Milestone: 10.0
Assignee: Martin Sebor
URL:
Keywords: missed-optimization, patch
Depends on:
Blocks: strlen
  Show dependency treegraph
 
Reported: 2019-06-13 22:18 UTC by Martin Sebor
Modified: 2019-11-07 16:46 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2019-06-14 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Sebor 2019-06-13 22:18:47 UTC
Here's another strcmp optimization opportunity (besides pr90626 and pr90876) that could easily be taken advantage of on top of the solution for pr83026.

When strncmp is called with a bound in excess of the sizes of the pointed-to arrays, GCC eliminates the pointless excessive bound and transforms the strncpy call to strcpy (in a subset of the cases when this is possible).  This can be seen in function f in the test case below.

But when, in a call to strcmp or strncmp, a string argument is longer than the size of the array it's being compared with for equality, the optimization fails to take advantage of the fact that a string that long cannot be equal to one stored in a smaller array.  This can be seen in function g in the test case.

The existing optimization has all the smarts to go this extra step, it just doesn't do it.

$ cat a.c && gcc -O2 -S -Wall -Wextra -fdump-tree-optimized=/dev/stdout a.c
extern char a[4];

void f (void)
{
  if (0 == __builtin_strncmp (a, "12345", 8))   // transformed to strcmp
    __builtin_abort ();                         // when the whole expression could be folded to zero
}

void g (void)
{ 
  if (0 == __builtin_strcmp (a, "123456"))      // not folded
    __builtin_abort ();
}

;; Function f (f, funcdef_no=0, decl_uid=1907, cgraph_uid=1, symbol_order=0)

f ()
{
  int _1;

  <bb 2> [local count: 1073741824]:
  _1 = __builtin_strcmp (&a, "12345");
  if (_1 == 0)
    goto <bb 3>; [0.00%]
  else
    goto <bb 4>; [100.00%]

  <bb 3> [count: 0]:
  __builtin_abort ();

  <bb 4> [local count: 1073741824]:
  return;

}



;; Function g (g, funcdef_no=1, decl_uid=1910, cgraph_uid=2, symbol_order=1)

g ()
{
  int _1;

  <bb 2> [local count: 1073741824]:
  _1 = __builtin_strcmp (&a, "123456");
  if (_1 == 0)
    goto <bb 3>; [0.00%]
  else
    goto <bb 4>; [100.00%]

  <bb 3> [count: 0]:
  __builtin_abort ();

  <bb 4> [local count: 1073741824]:
  return;

}
Comment 1 Richard Biener 2019-06-14 07:29:19 UTC
Confirmed.
Comment 2 Martin Sebor 2019-08-09 16:19:41 UTC
Patch: https://gcc.gnu.org/ml/gcc-patches/2019-08/msg00640.html
Comment 3 Martin Sebor 2019-10-09 21:35:42 UTC
Author: msebor
Date: Wed Oct  9 21:35:11 2019
New Revision: 276773

URL: https://gcc.gnu.org/viewcvs?rev=276773&root=gcc&view=rev
Log:
PR tree-optimization/90879 - fold zero-equality of strcmp between a longer string and a smaller array

gcc/c-family/ChangeLog:

	PR tree-optimization/90879
	* c.opt (-Wstring-compare): New option.

gcc/testsuite/ChangeLog:

	PR tree-optimization/90879
	* gcc.dg/Wstring-compare-2.c: New test.
	* gcc.dg/Wstring-compare.c: New test.
	* gcc.dg/strcmpopt_3.c: Scan the optmized dump instead of strlen.
	* gcc.dg/strcmpopt_6.c: New test.
	* gcc.dg/strlenopt-65.c: Remove uinnecessary declarations, add
	test cases.
	* gcc.dg/strlenopt-66.c: Run it.
	* gcc.dg/strlenopt-68.c: New test.

gcc/ChangeLog:

	PR tree-optimization/90879
	* builtins.c (check_access): Avoid using maxbound when null.
	* calls.c (maybe_warn_nonstring_arg): Adjust to get_range_strlen change.
	* doc/invoke.texi (-Wstring-compare): Document new warning option.
	* gimple-fold.c (get_range_strlen_tree): Make setting maxbound
	conditional.
	(get_range_strlen): Overwrite initial maxbound when non-null.
	* gimple-ssa-sprintf.c (get_string_length): Adjust to get_range_strlen
	changes.
	* tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Same.
	(used_only_for_zero_equality): New function.
	(handle_builtin_memcmp): Call it.
	(determine_min_objsize): Return an integer instead of tree.
	(get_len_or_size, strxcmp_eqz_result): New functions.
	(maybe_warn_pointless_strcmp): New function.
	(handle_builtin_string_cmp): Call it.  Fold zero-equality of strcmp
	between a longer string and a smaller array.
	(get_range_strlen_dynamic): Overwrite initial maxbound when non-null.


Added:
    trunk/gcc/testsuite/gcc.dg/Wstring-compare-2.c
    trunk/gcc/testsuite/gcc.dg/Wstring-compare.c
    trunk/gcc/testsuite/gcc.dg/strcmpopt_6.c
    trunk/gcc/testsuite/gcc.dg/strlenopt-69.c
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/builtins.c
    trunk/gcc/c-family/ChangeLog
    trunk/gcc/c-family/c.opt
    trunk/gcc/calls.c
    trunk/gcc/doc/invoke.texi
    trunk/gcc/gimple-fold.c
    trunk/gcc/gimple-ssa-sprintf.c
    trunk/gcc/testsuite/ChangeLog
    trunk/gcc/testsuite/gcc.dg/strcmpopt_3.c
    trunk/gcc/testsuite/gcc.dg/strlenopt-65.c
    trunk/gcc/testsuite/gcc.dg/strlenopt-66.c
    trunk/gcc/tree-ssa-strlen.c
Comment 4 Martin Sebor 2019-10-09 21:36:04 UTC
Patch committed in r276773.