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

Andrea Pinski <[email protected]> Sun, 2 Aug 2026 02:01:06 -0700
Newsgroups gmane.comp.gcc.patches
Message-ID <CALvbMcBeye19qNtaPf9jNkiuWdzDenVGZOTvqdH9Zh+XdcD8Sg@mail.gmail.com>
On Fri, Jul 31, 2026 at 5:08 AM Tudor-Stefan Magirescu
<[email protected]> wrote:
>
> 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.

Instead of adding a new target hook. I think it is just better to look
to see if there is byteswap optab for that mode.
That is just `can_open_code_p (bswap_optab, mode)`.
This should simplify the code here too.

Thanks,
Andrea

>
> 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 (): 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
>