[PATCH 1/1] forwprop: Extend memcmp forwprop inlining to the general case

Tudor-Stefan Magirescu <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
Extend simplify_builtin_memcmp to inline memcmp calls regardless of how
the result is used, not just when the result is compared with zero for
equality.  The call is inlined when the length argument is a small constant
power-of-two such that the loaded arguments fit in a machine word, emitting
a branchless signed comparison sequence instead.

On little-endian targets, the loaded value must be byte-swapped before
comparison.  The hook TARGET_MEMCMP_INLINE_USING_BSWAP_P gates this
optimization.  It defaults to false and should be overridden by targets with
an efficient byte-swap instruction.  x86 and AArch64 override the hook to
enable the optimization.

On big-endian targets, no byte-swap is required and the optimization is
enabled unconditionally when the size is appropriate.

Bootstrapped and tested on x86_64-linux-gnu.

gcc/ChangeLog:

	* config/aarch64/aarch64.cc (aarch64_memcmp_inline_using_bswap_p): New function.
	(TARGET_MEMCMP_INLINE_USING_BSWAP_P): New macro.
	* config/i386/i386.cc (ix86_memcmp_inline_using_bswap_p): New function.
	(TARGET_MEMCMP_INLINE_USING_BSWAP_P): New macro.
	* doc/tm.texi: Regenerate.
	* doc/tm.texi.in: Add hook TARGET_MEMCMP_INLINE_USING_BSWAP_P.
	* target.def (memcmp_inline_using_bswap_p): New hook.
	* tree-ssa-forwprop.cc (create_builtin_bswap): New function.
	(simplify_builtin_memcmp): Generalize memcmp inlining beyond equality with 0.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-stringop-chk-8.c: Adjust for new memcmp inlining.
	* gcc.target/aarch64/memcmp-rev-1.c: New test.
	* gcc.target/i386/memcmp-bswap-1.c: New test.
	* gcc.target/i386/memcmp-bswap-2.c: New test.
	* gcc.target/powerpc/memcmp-inline-1.c: New test.

Signed-off-by: Tudor-Stefan Magirescu <[email protected]>
---
 gcc/config/aarch64/aarch64.cc                 | 19 +++++
 gcc/config/i386/i386.cc                       | 22 +++++
 gcc/doc/tm.texi                               |  7 ++
 gcc/doc/tm.texi.in                            |  2 +
 gcc/target.def                                |  9 ++
 gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c |  2 +-
 .../gcc.target/aarch64/memcmp-rev-1.c         | 26 ++++++
 .../gcc.target/i386/memcmp-bswap-1.c          | 14 ++++
 .../gcc.target/i386/memcmp-bswap-2.c          | 13 +++
 .../gcc.target/powerpc/memcmp-inline-1.c      | 24 ++++++
 gcc/tree-ssa-forwprop.cc                      | 84 +++++++++++++++++--
 11 files changed, 213 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/memcmp-rev-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/memcmp-bswap-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/memcmp-bswap-2.c
 create mode 100644 gcc/testsuite/gcc.target/powerpc/memcmp-inline-1.c

diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index a1f91dd425e..4785cae9234 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -30818,6 +30818,22 @@ aarch64_promoted_type (const_tree t)
   return NULL_TREE;
 }
 
+/* Implement the TARGET_MEMCMP_INLINE_USING_BSWAP_P hook.  */
+
+static bool
+aarch64_memcmp_inline_using_bswap_p (machine_mode mode)
+{
+  switch (mode)
+    {
+    case HImode:
+    case SImode:
+    case DImode:
+      return true;
+    default:
+      return false;
+    }
+}
+
 /* Implement the TARGET_OPTAB_SUPPORTED_P hook.  */
 
 static bool
@@ -34373,6 +34389,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA
 #define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA aarch64_output_addr_const_extra
 
+#undef TARGET_MEMCMP_INLINE_USING_BSWAP_P
+#define TARGET_MEMCMP_INLINE_USING_BSWAP_P aarch64_memcmp_inline_using_bswap_p
+
 #undef TARGET_OPTAB_SUPPORTED_P
 #define TARGET_OPTAB_SUPPORTED_P aarch64_optab_supported_p
 
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index 23f65ab401d..94c297131f3 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -11419,6 +11419,24 @@ ix86_use_by_pieces_infrastructure_p (unsigned HOST_WIDE_INT size,
   return default_use_by_pieces_infrastructure_p (size, align, op,
 						 speed_p);
 }
+
+/* Implement the TARGET_MEMCMP_INLINE_USING_BSWAP_P hook.  */
+
+static bool
+ix86_memcmp_inline_using_bswap_p (machine_mode mode)
+{
+  switch (mode)
+    {
+    case HImode:
+      return true;
+    case SImode:
+      return TARGET_BSWAP;
+    case DImode:
+      return TARGET_BSWAP && TARGET_64BIT;
+    default:
+      return false;
+    }
+}
 
 /* Allow {LABEL | SYMBOL}_REF - SYMBOL_REF-FOR-PICBASE for Mach-O as
    this is used for to form addresses to local data when -fPIC is in
@@ -28563,6 +28581,10 @@ static const scoped_attribute_specs *const ix86_attribute_table[] =
 #define TARGET_USE_BY_PIECES_INFRASTRUCTURE_P \
   ix86_use_by_pieces_infrastructure_p
 
+#undef TARGET_MEMCMP_INLINE_USING_BSWAP_P
+#define TARGET_MEMCMP_INLINE_USING_BSWAP_P \
+  ix86_memcmp_inline_using_bswap_p
+
 #undef TARGET_OVERLAP_OP_BY_PIECES_P
 #define TARGET_OVERLAP_OP_BY_PIECES_P hook_bool_void_true
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 85e17525683..c28a195cac7 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -7177,6 +7177,13 @@ The hook must return true whenever @code{STRICT_ALIGNMENT} is true.
 The default implementation returns @code{STRICT_ALIGNMENT}.
 @end deftypefn
 
+@deftypefn {Target Hook} bool TARGET_MEMCMP_INLINE_USING_BSWAP_P (machine_mode @var{mode})
+Return true if @code{memcmp} calls for blocks of @var{mode} size
+should be inlined using a load, byte-swap and integer comparison
+sequence.  This is only profitable on targets with an efficient
+byte-swap instruction.  The default is false.
+@end deftypefn
+
 @defmac MOVE_RATIO (@var{speed})
 The threshold of number of scalar memory-to-memory move insns, @emph{below}
 which a sequence of insns should be generated instead of a
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 1a9edd0635d..aa94c616d46 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -4653,6 +4653,8 @@ other fields in the same word of the structure, but to different bytes.
 
 @hook TARGET_SLOW_UNALIGNED_ACCESS
 
+@hook TARGET_MEMCMP_INLINE_USING_BSWAP_P
+
 @defmac MOVE_RATIO (@var{speed})
 The threshold of number of scalar memory-to-memory move insns, @emph{below}
 which a sequence of insns should be generated instead of a
diff --git a/gcc/target.def b/gcc/target.def
index 884fe1bd57e..065b8b0663e 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4005,6 +4005,15 @@ The default implementation returns @code{STRICT_ALIGNMENT}.",
  bool, (machine_mode mode, unsigned int align),
  default_slow_unaligned_access)
 
+DEFHOOK
+(memcmp_inline_using_bswap_p,
+ "Return true if @code{memcmp} calls for blocks of @var{mode} size\n\
+should be inlined using a load, byte-swap and integer comparison\n\
+sequence.  This is only profitable on targets with an efficient\n\
+byte-swap instruction.  The default is false.",
+ bool, (machine_mode mode),
+ hook_bool_mode_false)
+
 DEFHOOK
 (optab_supported_p,
  "Return true if the optimizers should use optab @var{op} with\n\
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c
index f2e9c481172..bd3cbb64bbd 100644
--- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-8.c
@@ -78,7 +78,7 @@ void test_memop_warn_local (void *p, const void *q)
   memchr ("123", i, 5);   /* { dg-warning "specified bound 5 exceeds source size 4" "memchr" } */
   memchr (a, i, sizeof a + 1);   /* { dg-warning "specified bound 5 exceeds source size 4" "memchr" } */
 
-  memcmp (p, "", 2);   /* { dg-warning "specified bound 2 exceeds source size 1" "memcmp" } */
+  memcmp (p, "", 3);   /* { dg-warning "specified bound 3 exceeds source size 1" "memcmp" } */
   memcmp (p, "123", 5);   /* { dg-warning "specified bound 5 exceeds source size 4" "memcmp" } */
   memcmp (p, a, sizeof a + 1);   /* { dg-warning "specified bound 5 exceeds source size 4" "memcmp" } */
 
diff --git a/gcc/testsuite/gcc.target/aarch64/memcmp-rev-1.c b/gcc/testsuite/gcc.target/aarch64/memcmp-rev-1.c
new file mode 100644
index 00000000000..bc3212d79e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/memcmp-rev-1.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#include <string.h>
+
+_Bool
+cmp16 (const void* a, const void* b)
+{
+  return memcmp (a, b, 2) < 0;
+}
+
+_Bool
+cmp32 (const void* a, const void* b)
+{
+  return memcmp (a, b, 4) > 0;
+}
+
+int
+cmp64 (const void* a, const void* b)
+{
+  return memcmp (a, b, 8);
+}
+
+/* { dg-final { scan-assembler-times "rev16" 2  } } */
+/* { dg-final { scan-assembler-times "rev\t" 4  } } */
+/* { dg-final { scan-assembler-not "bl.*memcmp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/memcmp-bswap-1.c b/gcc/testsuite/gcc.target/i386/memcmp-bswap-1.c
new file mode 100644
index 00000000000..7646c01ce1c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/memcmp-bswap-1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-skip-if "" { *-*-* } { "-march=i386" } } */
+
+#include <string.h>
+
+_Bool
+cmp32 (const void* a, const void* b)
+{
+  return memcmp (a, b, 4) < 0;
+}
+
+/* { dg-final { scan-assembler-times "\tbswap\t%e" 2  } } */
+/* { dg-final { scan-assembler-not "call.*memcmp" } } */
diff --git a/gcc/testsuite/gcc.target/i386/memcmp-bswap-2.c b/gcc/testsuite/gcc.target/i386/memcmp-bswap-2.c
new file mode 100644
index 00000000000..dadb1153896
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/memcmp-bswap-2.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=x86-64" } */
+
+#include <string.h>
+
+_Bool
+cmp64 (const void* a, const void* b)
+{
+  return memcmp (a, b, 8) < 0;
+}
+
+/* { dg-final { scan-assembler-times "\tbswap\t%r" 2  } } */
+/* { dg-final { scan-assembler-not "call.*memcmp" } } */
diff --git a/gcc/testsuite/gcc.target/powerpc/memcmp-inline-1.c b/gcc/testsuite/gcc.target/powerpc/memcmp-inline-1.c
new file mode 100644
index 00000000000..53da98ae23e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/memcmp-inline-1.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+#include <string.h>
+
+_Bool
+cmp16 (const void* a, const void* b)
+{
+  return memcmp (a, b, 2) < 0;
+}
+
+_Bool
+cmp32 (const void* a, const void* b)
+{
+  return memcmp (a, b, 4) > 0;
+}
+
+int
+cmp64 (const void* a, const void* b)
+{
+  return memcmp (a, b, 8);
+}
+
+/* { dg-final { scan-assembler-not "bl.*memcmp" } } */
diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
index 12c07c99c9b..f16adac76c2 100644
--- a/gcc/tree-ssa-forwprop.cc
+++ b/gcc/tree-ssa-forwprop.cc
@@ -1981,6 +1981,31 @@ do_simple_agr_dse (gassign *stmt, bool full_walk)
     }
 }
 
+/* Return the bswap builtin for LEN bits, or NULL_TREE if the target
+   cannot expand it.  */
+
+static tree
+create_builtin_bswap (unsigned HOST_WIDE_INT len)
+{
+  switch (len)
+    {
+    case 16:
+      return (builtin_decl_explicit_p (BUILT_IN_BSWAP16)
+	      && can_open_code_p (bswap_optab, HImode))
+	      ? builtin_decl_explicit (BUILT_IN_BSWAP16) : NULL_TREE;
+    case 32:
+      return (builtin_decl_explicit_p (BUILT_IN_BSWAP32)
+	      && can_open_code_p (bswap_optab, SImode))
+	      ? builtin_decl_explicit (BUILT_IN_BSWAP32) : NULL_TREE;
+    case 64:
+      return (builtin_decl_explicit_p (BUILT_IN_BSWAP64)
+	      && can_open_code_p (bswap_optab, DImode))
+	      ? builtin_decl_explicit (BUILT_IN_BSWAP64) : NULL_TREE;
+    default:
+      return NULL_TREE;
+    }
+}
+
 /* Optimizes builtin memcmps for small constant sizes.
    GSI_P is the GSI for the call. STMT is the call itself.
    */
@@ -2006,8 +2031,9 @@ simplify_builtin_memcmp (gimple_stmt_iterator *gsi_p, gcall *stmt)
      equality comparison to zero. */
   tree res = gimple_call_lhs (stmt);
 
-  if (!res || !use_in_zero_equality (res))
+  if (!res)
     return false;
+  bool use_in_zero_eq = use_in_zero_equality (res);
 
   unsigned HOST_WIDE_INT leni;
 
@@ -2046,16 +2072,58 @@ simplify_builtin_memcmp (gimple_stmt_iterator *gsi_p, gcall *stmt)
 	  tree tem2 = fold_const_aggregate_ref (arg2);
 	  if (tem2)
 	    arg2 = tem2;
-	  res = fold_convert_loc (loc, TREE_TYPE (res),
-				  fold_build2_loc (loc, NE_EXPR,
-						   boolean_type_node,
-						   arg1, arg2));
-	  gimplify_and_update_call_from_tree (gsi_p, res);
-	  return true;
+
+	  if (use_in_zero_eq)
+	    {
+	      res = fold_convert_loc (loc, TREE_TYPE (res),
+				      fold_build2_loc (loc, NE_EXPR,
+						       boolean_type_node,
+						       arg1, arg2));
+	      gimplify_and_update_call_from_tree (gsi_p, res);
+	      return true;
+	    }
+
+	  tree bswap_decl = NULL_TREE;
+	  bool bytes_reversal_required
+	    = !BYTES_BIG_ENDIAN && (leni > CHAR_TYPE_SIZE);
+	  if (!bytes_reversal_required
+	      || ((bswap_decl = create_builtin_bswap (leni))
+		   && targetm.memcmp_inline_using_bswap_p (mode)))
+	    {
+	      if (bytes_reversal_required)
+		{
+		  /* save_expr ensures bswap calls are not duplicated
+		     during gimplification.  */
+		  arg1 = save_expr (build_call_expr_loc (loc, bswap_decl,
+							 1, arg1));
+		  arg2 = save_expr (build_call_expr_loc (loc, bswap_decl,
+							 1, arg2));
+		}
+	      tree cmp_lt
+		= fold_convert_loc (loc, TREE_TYPE (res),
+				    build2_loc (loc, LT_EXPR,
+						boolean_type_node,
+						arg1, arg2));
+	      tree cmp_gt
+		= fold_convert_loc (loc, TREE_TYPE (res),
+				    build2_loc (loc, GT_EXPR,
+						boolean_type_node,
+						arg1, arg2));
+	      res = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (res),
+				     cmp_gt, cmp_lt);
+	      gimplify_and_update_call_from_tree (gsi_p, res);
+	      return true;
+	    }
+
+	  return false;
 	}
     }
 
-  /* Replace memcmp with memcmp_eq if the above fails. */
+  if (!use_in_zero_eq)
+    return false;
+
+  /* Fall back to replacing memcmp with memcmp_eq if the result is only used in
+     a comparison to zero and the above inlining attempt did not apply.  */
   if (DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)) == BUILT_IN_MEMCMP_EQ)
     return false;
   if (!fold_before_rtl_expansion_p ())
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.