From: Jason Merrill Date: Fri, 10 Jan 2020 20:38:34 +0000 (-0500) Subject: PR c++/80265 - constexpr __builtin_mem*. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=69dc042f91c70458ffb6e7b147f093799cee2100;p=gcc.git PR c++/80265 - constexpr __builtin_mem*. The library has already worked around this issue, but I was curious about why it wasn't working. The answer: because we were passing &var to fold, which doesn't know about the constexpr values hash table. Fixed by passing &"str" instead. * constexpr.c (cxx_eval_builtin_function_call): Expose STRING_CST to str/mem builtins. --- diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 3ca3b9ecd65..3a9fb566a52 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1260,28 +1260,76 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, if (fndecl_built_in_p (fun, CP_BUILT_IN_SOURCE_LOCATION, BUILT_IN_FRONTEND)) return fold_builtin_source_location (EXPR_LOCATION (t)); + int strops = 0; + int strret = 0; + if (fndecl_built_in_p (fun, BUILT_IN_NORMAL)) + switch (DECL_FUNCTION_CODE (fun)) + { + case BUILT_IN_STRLEN: + case BUILT_IN_STRNLEN: + strops = 1; + break; + case BUILT_IN_MEMCHR: + case BUILT_IN_STRCHR: + case BUILT_IN_STRRCHR: + strops = 1; + strret = 1; + break; + case BUILT_IN_MEMCMP: + case BUILT_IN_STRCMP: + strops = 2; + break; + case BUILT_IN_STRSTR: + strops = 2; + strret = 1; + default: + break; + } + /* Be permissive for arguments to built-ins; __builtin_constant_p should return constant false for a non-constant argument. */ constexpr_ctx new_ctx = *ctx; new_ctx.quiet = true; for (i = 0; i < nargs; ++i) { - args[i] = CALL_EXPR_ARG (t, i); + tree arg = CALL_EXPR_ARG (t, i); + + /* To handle string built-ins we need to pass ADDR_EXPR since + expand_builtin doesn't know how to look in the values table. */ + bool strop = i < strops; + if (strop) + { + STRIP_NOPS (arg); + if (TREE_CODE (arg) == ADDR_EXPR) + arg = TREE_OPERAND (arg, 0); + else + strop = false; + } + /* If builtin_valid_in_constant_expr_p is true, potential_constant_expression_1 has not recursed into the arguments of the builtin, verify it here. */ if (!builtin_valid_in_constant_expr_p (fun) - || potential_constant_expression (args[i])) + || potential_constant_expression (arg)) { bool dummy1 = false, dummy2 = false; - args[i] = cxx_eval_constant_expression (&new_ctx, args[i], false, - &dummy1, &dummy2); + arg = cxx_eval_constant_expression (&new_ctx, arg, false, + &dummy1, &dummy2); } if (bi_const_p) /* For __builtin_constant_p, fold all expressions with constant values even if they aren't C++ constant-expressions. */ - args[i] = cp_fold_rvalue (args[i]); + arg = cp_fold_rvalue (arg); + else if (strop) + { + if (TREE_CODE (arg) == CONSTRUCTOR) + arg = braced_lists_to_strings (TREE_TYPE (arg), arg); + if (TREE_CODE (arg) == STRING_CST) + arg = build_address (arg); + } + + args[i] = arg; } bool save_ffbcp = force_folding_builtin_constant_p; @@ -1325,6 +1373,18 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, return t; } + if (strret) + { + /* memchr returns a pointer into the first argument, but we replaced the + argument above with a STRING_CST; put it back it now. */ + tree op = CALL_EXPR_ARG (t, strret-1); + STRIP_NOPS (new_call); + if (TREE_CODE (new_call) == POINTER_PLUS_EXPR) + TREE_OPERAND (new_call, 0) = op; + else if (TREE_CODE (new_call) == ADDR_EXPR) + new_call = op; + } + return cxx_eval_constant_expression (&new_ctx, new_call, lval, non_constant_p, overflow_p); } diff --git a/gcc/testsuite/g++.dg/ext/constexpr-builtin1.C b/gcc/testsuite/g++.dg/ext/constexpr-builtin1.C new file mode 100644 index 00000000000..2d0904b6e08 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/constexpr-builtin1.C @@ -0,0 +1,37 @@ +// PR c++/80265 +// { dg-do compile { target c++14 } } + +constexpr bool compare() { + char s1[] = "foo"; + char s2[] = "fxo"; + if (!__builtin_memcmp(s1, s2, 3)) + return false; + s2[1] = 'o'; + if (__builtin_memcmp(s1, s2, 3)) + return false; + if (__builtin_strcmp(s1, s2)) + return false; + return true; +} + +constexpr bool length() { + char s[] = "foo"; + if (__builtin_strlen(s) != 3) + return false; + return true; +} + +constexpr bool find() { + char s[] = "foo"; + if (__builtin_memchr(s, 'f', 3) != s) + return false; + if (__builtin_strchr(s, 'o') != s+1) + return false; + if (__builtin_strstr(s, "oo") != s+1) + return false; + return true; +} + +static_assert( compare(), "" ); +static_assert( length(), "" ); +static_assert( find(), "" );