aboutsummaryrefslogtreecommitdiff
path: root/gcc/c/c-parser.cc
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2024-10-29 09:06:25 +0100
committerJakub Jelinek <jakub@gcc.gnu.org>2024-10-29 09:06:25 +0100
commit972f653cad2aedcfa901614566506c1c2e668766 (patch)
tree49c17bb7190fbf3777de58d816c46d4665c2f9d9 /gcc/c/c-parser.cc
parent87fa88222ff0c7f8b2ca3a480f41fc4e826cdf32 (diff)
downloadgcc-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.cc85
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)
{