https://gcc.gnu.org/g:54dd6575fa5ba7554396039820bee88d4dddab6d
commit r17-2184-g54dd6575fa5ba7554396039820bee88d4dddab6d Author: Naveen <[email protected]> Date: Tue Jul 7 01:41:44 2026 -0700 tree-optimization: Extend CDCE memset shrink-wrapping to {0, N} ranges [PR102202] The patch extends the CDCE shrink-wrapping support added for PR102202. The existing transform handles memset calls whose length is known to be in [0, 1]. It bypasses the call for length zero and specializes the nonzero path to a constant length of one. Extend this to exact two-value length ranges {0, N}, where N is a nonzero integer constant. The zero-length path is still bypassed and the nonzero path is specialized to the constant N. In particular, the transform does not accept an ordinary interval [0, N] since such an interval can contain values other than zero and N. The range query now returns the nonzero member of either a [0, 1] range or an exact {0, N} range. The existing [0, 1] behavior is preserved. gcc/ChangeLog: PR tree-optimization/102202 * tree-call-cdce.cc: Include "value-query.h" and "value-range.h". (len_has_boolean_range_p): Remove. (get_len_nonzero_value): New function. Return the nonzero member of LEN's value range when it is a boolean range [0, 1] or an exact two-value set {0, N}. (can_shrink_wrap_len_p): Replace len_has_boolean_range_p call with get_len_nonzero_value. Add NONZERO_LEN output parameter and set it to the returned nonzero value on success. (shrink_wrap_len_call): Use it to pin the length argument on the guarded call path instead of build_one_cst. (shrink_wrap_conditional_dead_built_in_calls): Declare local nonzero_len and pass it to can_shrink_wrap_len_p and forward it to shrink_wrap_len_call. (pass_call_cdce::execute): Pass nullptr as NONZERO_LEN to can_shrink_wrap_len_p. gcc/testsuite/ChangeLog: PR tree-optimization/102202 * gcc.dg/pr102202-3.c: New test. * gcc.dg/pr102202-4.c: New test. Signed-off-by: Naveen <[email protected]> Diff: --- gcc/testsuite/gcc.dg/pr102202-3.c | 21 +++++++++++++ gcc/testsuite/gcc.dg/pr102202-4.c | 16 ++++++++++ gcc/tree-call-cdce.cc | 65 +++++++++++++++++++++++++++------------ 3 files changed, 82 insertions(+), 20 deletions(-) diff --git a/gcc/testsuite/gcc.dg/pr102202-3.c b/gcc/testsuite/gcc.dg/pr102202-3.c new file mode 100644 index 000000000000..1904e309fee6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr102202-3.c @@ -0,0 +1,21 @@ +/* PR tree-optimization/102202 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-cdce-details" } */ + +/* An exact two-value range {0, 2}. Use a PHI so that Ranger retains + the two singleton values at the call. */ + +void +g1 (unsigned int x, int c, char *d) +{ + __SIZE_TYPE__ len; + + if (x) + len = 2; + else + len = 0; + + __builtin_memset (d, c, len); +} + +/* { dg-final { scan-tree-dump-times "function call is shrink-wrapped into error conditions" 1 "cdce" } } */ diff --git a/gcc/testsuite/gcc.dg/pr102202-4.c b/gcc/testsuite/gcc.dg/pr102202-4.c new file mode 100644 index 000000000000..908256b5b2d1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr102202-4.c @@ -0,0 +1,16 @@ +/* PR tree-optimization/102202 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-cdce-details" } */ + +/* The interval [0, 2] is not the exact two-value set {0, 2}. */ + +void +g1 (__SIZE_TYPE__ len, int c, char *d) +{ + if (len > 2) + __builtin_unreachable (); + + __builtin_memset (d, c, len); +} + +/* { dg-final { scan-tree-dump-not "function call is shrink-wrapped into error conditions" "cdce" } } */ diff --git a/gcc/tree-call-cdce.cc b/gcc/tree-call-cdce.cc index 144f485909cf..289504fc9273 100644 --- a/gcc/tree-call-cdce.cc +++ b/gcc/tree-call-cdce.cc @@ -39,6 +39,8 @@ along with GCC; see the file COPYING3. If not see #include "tree-eh.h" #include "tree-ssanames.h" #include "gimple-fold.h" +#include "value-query.h" +#include "value-range.h" /* This pass serves two closely-related purposes: @@ -1257,23 +1259,40 @@ use_internal_fn (gcall *call) is_arg_conds ? new_call : NULL); } -/* Return true if LEN is an SSA_NAME known to have a boolean range, i.e. its - value is provably in [0, 1]. */ +/* Return the nonzero member of LEN's range if LEN is an SSA name with range + [0, 1] or the exact two-value set {0, N}. Return NULL_TREE otherwise. */ -static bool -len_has_boolean_range_p (tree len, gimple *stmt) +static tree +get_len_nonzero_value (tree len, gimple *stmt) { if (TREE_CODE (len) != SSA_NAME || !INTEGRAL_TYPE_P (TREE_TYPE (len))) - return false; - return ssa_name_has_boolean_range (len, stmt); + return NULL_TREE; + + if (ssa_name_has_boolean_range (len, stmt)) + return build_one_cst (TREE_TYPE (len)); + + int_range<2> range (TREE_TYPE (len)); + if (!get_range_query (cfun)->range_of_expr (range, len, stmt) + || range.num_pairs () != 2) + return NULL_TREE; + + if (!wi::eq_p (range.lower_bound (0), 0) + || !wi::eq_p (range.upper_bound (0), 0)) + return NULL_TREE; + + if (!wi::eq_p (range.lower_bound (1), range.upper_bound (1))) + return NULL_TREE; + + return wide_int_to_tree (TREE_TYPE (len), range.lower_bound (1)); } /* Return true if CALL is a supported length-taking builtin whose length - argument is an SSA name known to have a boolean range. On success, - set LEN_ARG to the argument index of the length. */ + argument is an SSA name known to have a suitable range. On success, + set LEN_ARG to the argument index of the length and, if NONZERO_LEN is + nonnull, set it to the nonzero member of the range. */ static bool -can_shrink_wrap_len_p (gcall *call, unsigned *len_arg) +can_shrink_wrap_len_p (gcall *call, unsigned *len_arg, tree *nonzero_len) { if (!gimple_call_builtin_p (call, BUILT_IN_MEMSET) || !gimple_vdef (call)) @@ -1284,10 +1303,13 @@ can_shrink_wrap_len_p (gcall *call, unsigned *len_arg) return false; tree len = gimple_call_arg (call, memset_len_arg); - if (!len_has_boolean_range_p (len, call)) + tree value = get_len_nonzero_value (len, call); + if (!value) return false; *len_arg = memset_len_arg; + if (nonzero_len) + *nonzero_len = value; return true; } @@ -1305,13 +1327,14 @@ gen_zero_len_conditions (tree len, vec<gimple *> &conds, unsigned *nconds) *nconds = 1; } -/* Shrink-wrap CALL whose LEN_ARG argument is known to be in [0, 1]. - ZERO_LEN_RESULT is the value of the call result when its length is zero. - On the guarded path the length is one, so pin it to a constant for - subsequent folding. */ +/* Shrink-wrap CALL whose LEN_ARG argument is known to be in [0, 1] or + {0, N}. ZERO_LEN_RESULT is the value of the call result when its length + is zero. On the guarded path the length is NONZERO_LEN, so pin it to that + constant for subsequent folding. */ static void -shrink_wrap_len_call (gcall *call, unsigned len_arg, tree zero_len_result) +shrink_wrap_len_call (gcall *call, unsigned len_arg, tree zero_len_result, + tree nonzero_len) { tree lhs = gimple_call_lhs (call); @@ -1341,8 +1364,8 @@ shrink_wrap_len_call (gcall *call, unsigned len_arg, tree zero_len_result) shrink_wrap_one_built_in_call_with_conds (call, conds, nconds); - /* On the guarded path the length is one. */ - gimple_call_set_arg (call, len_arg, build_one_cst (TREE_TYPE (len))); + /* On the guarded path the length is NONZERO_LEN. */ + gimple_call_set_arg (call, len_arg, nonzero_len); update_stmt (call); gimple_stmt_iterator gsi = gsi_for_stmt (call); fold_stmt (&gsi); @@ -1363,8 +1386,10 @@ shrink_wrap_conditional_dead_built_in_calls (const vec<gcall *> &calls) unsigned len_arg; /* memset returns its destination pointer on the zero-length path. */ - if (can_shrink_wrap_len_p (bi_call, &len_arg)) - shrink_wrap_len_call (bi_call, len_arg, gimple_call_arg (bi_call, 0)); + tree nonzero_len; + if (can_shrink_wrap_len_p (bi_call, &len_arg, &nonzero_len)) + shrink_wrap_len_call (bi_call, len_arg, gimple_call_arg (bi_call, 0), + nonzero_len); else if (gimple_call_lhs (bi_call)) use_internal_fn (bi_call); else @@ -1427,7 +1452,7 @@ pass_call_cdce::execute (function *fun) unsigned len_arg; if (stmt && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL) - && (can_shrink_wrap_len_p (stmt, &len_arg) + && (can_shrink_wrap_len_p (stmt, &len_arg, nullptr) || (gimple_call_lhs (stmt) ? can_use_internal_fn (stmt) : can_test_argument_range (stmt)))
