diff options
author | Jakub Jelinek <jakub@redhat.com> | 2024-10-29 09:06:25 +0100 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2024-10-29 09:06:25 +0100 |
commit | 972f653cad2aedcfa901614566506c1c2e668766 (patch) | |
tree | 49c17bb7190fbf3777de58d816c46d4665c2f9d9 /gcc/c/c-parser.cc | |
parent | 87fa88222ff0c7f8b2ca3a480f41fc4e826cdf32 (diff) | |
download | gcc-972f653cad2aedcfa901614566506c1c2e668766.zip gcc-972f653cad2aedcfa901614566506c1c2e668766.tar.gz gcc-972f653cad2aedcfa901614566506c1c2e668766.tar.bz2 |
c: Add __builtin_stdc_rotate_{left,right} builtins [PR117030]
I believe the new C2Y <stdbit.h> type-generic functions
stdc_rotate_{left,right} have the same problems the other stdc_*
type-generic functions had. If we want to support arbitrary
unsigned _BitInt(N), don't want to use statement expressions
(so that one can actually use them in static variable initializers),
don't want to evaluate the arguments multiple times and don't want
to expand the arguments multiple times during preprocessing to avoid the
old tgmath preprocessing bloat, we need a built-in for those.
The following patch adds those. And as we need to support rotations by 0
and tree-ssa-forwprop.cc is only able to pattern recognize with BIT_AND_EXPR
for that case (i.e. for power of two widths), the patch just constructs
LROTATE_EXPR/RROTATE_EXPR right away. Negative second arguments are
considered UB, while positive ones are modulo precision.
2024-10-29 Jakub Jelinek <jakub@redhat.com>
PR c/117030
gcc/
* doc/extend.texi (__builtin_stdc_rotate_left,
__builtin_stdc_rotate_right): Document.
gcc/c-family/
* c-common.cc (c_common_reswords): Add __builtin_stdc_rotate_left
and __builtin_stdc_rotate_right.
* c-ubsan.cc (ubsan_instrument_shift): For {L,R}ROTATE_EXPR
just check if op1 is negative.
gcc/c/
* c-parser.cc: Include asan.h and c-family/c-ubsan.h.
(c_parser_postfix_expression): Handle __builtin_stdc_rotate_left
and __builtin_stdc_rotate_right.
* c-fold.cc (c_fully_fold_internal): Handle LROTATE_EXPR and
RROTATE_EXPR.
gcc/testsuite/
* gcc.dg/builtin-stdc-rotate-1.c: New test.
* gcc.dg/builtin-stdc-rotate-2.c: New test.
* gcc.dg/ubsan/builtin-stdc-rotate-1.c: New test.
* gcc.dg/ubsan/builtin-stdc-rotate-2.c: New test.
Diffstat (limited to 'gcc/c/c-parser.cc')
-rw-r--r-- | gcc/c/c-parser.cc | 85 |
1 files changed, 84 insertions, 1 deletions
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 195cace..88dd0be 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -74,6 +74,8 @@ along with GCC; see the file COPYING3. If not see #include "bitmap.h" #include "analyzer/analyzer-language.h" #include "toplev.h" +#include "asan.h" +#include "c-family/c-ubsan.h" /* We need to walk over decls with incomplete struct/union/enum types after parsing the whole translation unit. @@ -12262,12 +12264,15 @@ c_parser_postfix_expression (c_parser *parser) C_BUILTIN_STDC_HAS_SINGLE_BIT, C_BUILTIN_STDC_LEADING_ONES, C_BUILTIN_STDC_LEADING_ZEROS, + C_BUILTIN_STDC_ROTATE_LEFT, + C_BUILTIN_STDC_ROTATE_RIGHT, C_BUILTIN_STDC_TRAILING_ONES, C_BUILTIN_STDC_TRAILING_ZEROS, C_BUILTIN_STDC_MAX } stdc_rid = C_BUILTIN_STDC_MAX; const char *name = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); + unsigned num_args = 1; switch (name[sizeof ("__builtin_stdc_") - 1]) { case 'b': @@ -12316,6 +12321,13 @@ c_parser_postfix_expression (c_parser *parser) else stdc_rid = C_BUILTIN_STDC_LEADING_ZEROS; break; + case 'r': + if (name[sizeof ("__builtin_stdc_rotate_") - 1] == 'l') + stdc_rid = C_BUILTIN_STDC_ROTATE_LEFT; + else + stdc_rid = C_BUILTIN_STDC_ROTATE_RIGHT; + num_args = 2; + break; case 't': if (name[sizeof ("__builtin_stdc_trailing_") - 1] == 'o') stdc_rid = C_BUILTIN_STDC_TRAILING_ONES; @@ -12334,7 +12346,7 @@ c_parser_postfix_expression (c_parser *parser) break; } - if (vec_safe_length (cexpr_list) != 1) + if (vec_safe_length (cexpr_list) != num_args) { error_at (loc, "wrong number of arguments to %qs", name); expr.set_error (); @@ -12406,6 +12418,77 @@ c_parser_postfix_expression (c_parser *parser) without evaluating arg multiple times, type being __typeof (arg) and prec __builtin_popcountg ((type) ~0)). */ int prec = TYPE_PRECISION (type); + if (num_args == 2) + { + /* Expand: + __builtin_stdc_rotate_left (arg1, arg2) as + arg1 r<< (arg2 % prec) + __builtin_stdc_rotate_right (arg1, arg2) as + arg1 r>> (arg2 % prec). */ + arg_p = &(*cexpr_list)[1]; + *arg_p = convert_lvalue_to_rvalue (loc, *arg_p, true, true); + if (!INTEGRAL_TYPE_P (TREE_TYPE (arg_p->value))) + { + error_at (loc, "%qs operand not an integral type", name); + expr.set_error (); + break; + } + if (TREE_CODE (TREE_TYPE (arg_p->value)) == ENUMERAL_TYPE) + { + error_at (loc, "argument %u in call to function " + "%qs has enumerated type", 2, name); + expr.set_error (); + break; + } + tree arg1 = save_expr (arg); + tree arg2 = save_expr (arg_p->value); + tree_code code; + if (stdc_rid == C_BUILTIN_STDC_ROTATE_LEFT) + code = LROTATE_EXPR; + else + code = RROTATE_EXPR; + + if (TREE_CODE (arg2) == INTEGER_CST + && tree_int_cst_sgn (arg2) < 0) + warning_at (loc, OPT_Wshift_count_negative, + "rotate count is negative"); + + tree instrument_expr = NULL_TREE; + if (sanitize_flags_p (SANITIZE_SHIFT)) + instrument_expr = ubsan_instrument_shift (loc, code, + arg1, arg2); + + /* Promote arg2 to unsigned just so that we don't + need to deal with arg2 type not being able to represent + prec. In the end gimplification uses unsigned int + for all shifts/rotates anyway. */ + if (TYPE_PRECISION (TREE_TYPE (arg2)) + < TYPE_PRECISION (integer_type_node)) + arg2 = fold_convert (unsigned_type_node, arg2); + + if (TYPE_UNSIGNED (TREE_TYPE (arg2))) + arg2 = build2_loc (loc, TRUNC_MOD_EXPR, TREE_TYPE (arg2), + arg2, build_int_cst (TREE_TYPE (arg2), + prec)); + else + { + /* When second argument is signed, just do the modulo in + unsigned type, that results in better generated code + (for power of 2 precisions bitwise AND). */ + tree utype = c_common_unsigned_type (TREE_TYPE (arg2)); + arg2 = build2_loc (loc, TRUNC_MOD_EXPR, utype, + fold_convert (utype, arg2), + build_int_cst (utype, prec)); + } + + expr.value = build2_loc (loc, code, TREE_TYPE (arg1), arg1, + arg2); + if (instrument_expr) + expr.value = build2_loc (loc, COMPOUND_EXPR, + TREE_TYPE (expr.value), + instrument_expr, expr.value); + break; + } tree barg1 = arg; switch (stdc_rid) { |