diff options
author | Jakub Jelinek <jakub@redhat.com> | 2023-11-23 10:12:30 +0100 |
---|---|---|
committer | Jakub Jelinek <jakub@redhat.com> | 2023-11-23 10:32:33 +0100 |
commit | 03c7149db6ec3c8a70baac1f8f8fa7d0773de3f0 (patch) | |
tree | 8b7420e49f0a881f35d415d5762e75a9fdcba03c /gcc/c | |
parent | 7758cb4b53e8a33642709402ce582f769eb9fd18 (diff) | |
download | gcc-03c7149db6ec3c8a70baac1f8f8fa7d0773de3f0.zip gcc-03c7149db6ec3c8a70baac1f8f8fa7d0773de3f0.tar.gz gcc-03c7149db6ec3c8a70baac1f8f8fa7d0773de3f0.tar.bz2 |
c: Add __builtin_stdc_* builtins
As discussed in the
https://sourceware.org/pipermail/libc-alpha/2023-November/152756.html
thread, including e.g.
https://sourceware.org/pipermail/libc-alpha/2023-November/152795.html
patch, while one can use the new __builtin_{clz,ctz,popcount}g builtins
to implement the stdbit.h type-generic macros, there are certain problems
with that implementation if those macros must be usable outside of
function bodies (e.g. int a = sizeof (stdc_bit_floor (0ULL));), must not
evaluate their arguments multiple times and especially for deep stdc_*
macro nesting don't expand the argument more than once. Plus ideally are
usable in constant expressions for all the types if they have constant
arguments. The above second URL satisfies it all but the last two (the
last one satisfies for many of them). While we could get away with just
adding __biultin_stdc_bit_{ceil,floor,width} which are complicated and
2 further extensions (some way to say that __builtin_c{l,t}zg should
imply bit precision of the first argument for the second argument without
using __builtin_popcountg ((__typeof (x)) -1) in there because that
causes another expansion of the macro argument and say __builtin_bit_complement
type-generic builtin which would be like (__typeof (x)) ~(x)), it was decided
we want to implement builtins for all the stdc type-generic macros.
As we are close to running out of 8-bit enum rid (when adding the 14 new
RID_* we have 7 too many), this patch implements those 14 keywords using
a single RID_BUILTIN_STDC and simply in the rare case this is being
parsed check values of 1-2 characters from the builtin names to see which
one it is.
2023-11-23 Jakub Jelinek <jakub@redhat.com>
gcc/
* doc/extend.texi (__builtin_stdc_bit_ceil, __builtin_stdc_bit_floor,
__builtin_stdc_bit_width, __builtin_stdc_count_ones,
__builtin_stdc_count_zeros, __builtin_stdc_first_leading_one,
__builtin_stdc_first_leading_zero, __builtin_stdc_first_trailing_one,
__builtin_stdc_first_trailing_zero, __builtin_stdc_has_single_bit,
__builtin_stdc_leading_ones, __builtin_stdc_leading_zeros,
__builtin_stdc_trailing_ones, __builtin_stdc_trailing_zeros): Document.
gcc/c-family/
* c-common.h (enum rid): Add RID_BUILTIN_STDC: New.
* c-common.cc (c_common_reswords): Add __builtin_stdc_bit_ceil,
__builtin_stdc_bit_floor, __builtin_stdc_bit_width,
__builtin_stdc_count_ones, __builtin_stdc_count_zeros,
__builtin_stdc_first_leading_one, __builtin_stdc_first_leading_zero,
__builtin_stdc_first_trailing_one, __builtin_stdc_first_trailing_zero,
__builtin_stdc_has_single_bit, __builtin_stdc_leading_ones,
__builtin_stdc_leading_zeros, __builtin_stdc_trailing_ones and
__builtin_stdc_trailing_zeros. Move __builtin_assoc_barrier
alphabetically earlier.
gcc/c/
* c-parser.cc (c_parser_postfix_expression): Handle RID_BUILTIN_STDC.
* c-decl.cc (names_builtin_p): Likewise.
gcc/testsuite/
* gcc.dg/builtin-stdc-bit-1.c: New test.
* gcc.dg/builtin-stdc-bit-2.c: New test.
Diffstat (limited to 'gcc/c')
-rw-r--r-- | gcc/c/c-decl.cc | 3 | ||||
-rw-r--r-- | gcc/c/c-parser.cc | 291 |
2 files changed, 293 insertions, 1 deletions
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 64d3a94..439a312 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -11370,11 +11370,12 @@ names_builtin_p (const char *name) functions. */ switch (C_RID_CODE (id)) { + case RID_BUILTIN_ASSOC_BARRIER: case RID_BUILTIN_CONVERTVECTOR: case RID_BUILTIN_HAS_ATTRIBUTE: case RID_BUILTIN_SHUFFLE: case RID_BUILTIN_SHUFFLEVECTOR: - case RID_BUILTIN_ASSOC_BARRIER: + case RID_BUILTIN_STDC: case RID_CHOOSE_EXPR: case RID_OFFSETOF: case RID_TYPES_COMPATIBLE_P: diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 703f957..371dd29 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -11743,6 +11743,297 @@ c_parser_postfix_expression (c_parser *parser) set_c_expr_source_range (&expr, start_loc, end_loc); } break; + case RID_BUILTIN_STDC: + { + vec<c_expr_t, va_gc> *cexpr_list; + c_expr_t *arg_p; + location_t close_paren_loc; + enum c_builtin_stdc { + C_BUILTIN_STDC_BIT_CEIL, + C_BUILTIN_STDC_BIT_FLOOR, + C_BUILTIN_STDC_BIT_WIDTH, + C_BUILTIN_STDC_COUNT_ONES, + C_BUILTIN_STDC_COUNT_ZEROS, + C_BUILTIN_STDC_FIRST_LEADING_ONE, + C_BUILTIN_STDC_FIRST_LEADING_ZERO, + C_BUILTIN_STDC_FIRST_TRAILING_ONE, + C_BUILTIN_STDC_FIRST_TRAILING_ZERO, + C_BUILTIN_STDC_HAS_SINGLE_BIT, + C_BUILTIN_STDC_LEADING_ONES, + C_BUILTIN_STDC_LEADING_ZEROS, + 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); + switch (name[sizeof ("__builtin_stdc_") - 1]) + { + case 'b': + switch (name[sizeof ("__builtin_stdc_bit_") - 1]) + { + case 'c': + stdc_rid = C_BUILTIN_STDC_BIT_CEIL; + break; + case 'f': + stdc_rid = C_BUILTIN_STDC_BIT_FLOOR; + break; + default: + stdc_rid = C_BUILTIN_STDC_BIT_WIDTH; + break; + } + break; + case 'c': + if (name[sizeof ("__builtin_stdc_count_") - 1] == 'o') + stdc_rid = C_BUILTIN_STDC_COUNT_ONES; + else + stdc_rid = C_BUILTIN_STDC_COUNT_ZEROS; + break; + case 'f': + switch (name[sizeof ("__builtin_stdc_first_trailing_") - 1]) + { + case 'n': + stdc_rid = C_BUILTIN_STDC_FIRST_LEADING_ONE; + break; + case 'e': + stdc_rid = C_BUILTIN_STDC_FIRST_LEADING_ZERO; + break; + case 'o': + stdc_rid = C_BUILTIN_STDC_FIRST_TRAILING_ONE; + break; + default: + stdc_rid = C_BUILTIN_STDC_FIRST_TRAILING_ZERO; + break; + } + break; + case 'h': + stdc_rid = C_BUILTIN_STDC_HAS_SINGLE_BIT; + break; + case 'l': + if (name[sizeof ("__builtin_stdc_leading_") - 1] == 'o') + stdc_rid = C_BUILTIN_STDC_LEADING_ONES; + else + stdc_rid = C_BUILTIN_STDC_LEADING_ZEROS; + break; + case 't': + if (name[sizeof ("__builtin_stdc_trailing_") - 1] == 'o') + stdc_rid = C_BUILTIN_STDC_TRAILING_ONES; + else + stdc_rid = C_BUILTIN_STDC_TRAILING_ZEROS; + break; + } + gcc_checking_assert (stdc_rid != C_BUILTIN_STDC_MAX); + + c_parser_consume_token (parser); + if (!c_parser_get_builtin_args (parser, name, + &cexpr_list, false, + &close_paren_loc)) + { + expr.set_error (); + break; + } + + if (vec_safe_length (cexpr_list) != 1) + { + error_at (loc, "wrong number of arguments to %qs", name); + expr.set_error (); + break; + } + + arg_p = &(*cexpr_list)[0]; + *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; + } + tree arg = arg_p->value; + tree type = TYPE_MAIN_VARIANT (TREE_TYPE (arg)); + /* Expand: + __builtin_stdc_leading_zeros (arg) as + (unsigned int) __builtin_clzg (arg, prec) + __builtin_stdc_leading_ones (arg) as + (unsigned int) __builtin_clzg ((type) ~arg, prec) + __builtin_stdc_trailing_zeros (arg) as + (unsigned int) __builtin_ctzg (arg, prec) + __builtin_stdc_trailing_ones (arg) as + (unsigned int) __builtin_ctzg ((type) ~arg, prec) + __builtin_stdc_first_leading_zero (arg) as + __builtin_clzg ((type) ~arg, -1) + 1U + __builtin_stdc_first_leading_one (arg) as + __builtin_clzg (arg, -1) + 1U + __builtin_stdc_first_trailing_zero (arg) as + __builtin_ctzg ((type) ~arg, -1) + 1U + __builtin_stdc_first_trailing_one (arg) as + __builtin_ctzg (arg, -1) + 1U + __builtin_stdc_count_zeros (arg) as + (unsigned int) __builtin_popcountg ((type) ~arg) + __builtin_stdc_count_ones (arg) as + (unsigned int) __builtin_popcountg (arg) + __builtin_stdc_has_single_bit (arg) as + (_Bool) (__builtin_popcountg (arg) == 1) + __builtin_stdc_bit_width (arg) as + (unsigned int) (prec - __builtin_clzg (arg, prec)) + __builtin_stdc_bit_floor (arg) as + arg == 0 ? (type) 0 + : (type) 1 << (prec - 1 - __builtin_clzg (arg)) + __builtin_stdc_bit_ceil (arg) as + arg <= 1 ? (type) 1 + : (type) 2 << (prec - 1 - __builtin_clzg (arg - 1)) + without evaluating arg multiple times, type being + __typeof (arg) and prec __builtin_popcountg ((type) ~0)). */ + int prec = TYPE_PRECISION (type); + tree barg1 = arg; + switch (stdc_rid) + { + case C_BUILTIN_STDC_BIT_CEIL: + arg = save_expr (arg); + barg1 = build2_loc (loc, PLUS_EXPR, type, arg, + build_int_cst (type, -1)); + break; + case C_BUILTIN_STDC_BIT_FLOOR: + barg1 = arg = save_expr (arg); + break; + case C_BUILTIN_STDC_COUNT_ZEROS: + case C_BUILTIN_STDC_FIRST_LEADING_ZERO: + case C_BUILTIN_STDC_FIRST_TRAILING_ZERO: + case C_BUILTIN_STDC_LEADING_ONES: + case C_BUILTIN_STDC_TRAILING_ONES: + barg1 = build1_loc (loc, BIT_NOT_EXPR, type, arg); + break; + default: + break; + } + tree barg2 = NULL_TREE; + switch (stdc_rid) + { + case C_BUILTIN_STDC_BIT_WIDTH: + case C_BUILTIN_STDC_LEADING_ONES: + case C_BUILTIN_STDC_LEADING_ZEROS: + case C_BUILTIN_STDC_TRAILING_ONES: + case C_BUILTIN_STDC_TRAILING_ZEROS: + barg2 = build_int_cst (integer_type_node, prec); + break; + case C_BUILTIN_STDC_FIRST_LEADING_ONE: + case C_BUILTIN_STDC_FIRST_LEADING_ZERO: + case C_BUILTIN_STDC_FIRST_TRAILING_ONE: + case C_BUILTIN_STDC_FIRST_TRAILING_ZERO: + barg2 = integer_minus_one_node; + break; + default: + break; + } + tree fndecl = NULL_TREE; + switch (stdc_rid) + { + case C_BUILTIN_STDC_BIT_CEIL: + case C_BUILTIN_STDC_BIT_FLOOR: + case C_BUILTIN_STDC_BIT_WIDTH: + case C_BUILTIN_STDC_FIRST_LEADING_ONE: + case C_BUILTIN_STDC_FIRST_LEADING_ZERO: + case C_BUILTIN_STDC_LEADING_ONES: + case C_BUILTIN_STDC_LEADING_ZEROS: + fndecl = builtin_decl_explicit (BUILT_IN_CLZG); + break; + case C_BUILTIN_STDC_FIRST_TRAILING_ONE: + case C_BUILTIN_STDC_FIRST_TRAILING_ZERO: + case C_BUILTIN_STDC_TRAILING_ONES: + case C_BUILTIN_STDC_TRAILING_ZEROS: + fndecl = builtin_decl_explicit (BUILT_IN_CTZG); + break; + case C_BUILTIN_STDC_COUNT_ONES: + case C_BUILTIN_STDC_COUNT_ZEROS: + case C_BUILTIN_STDC_HAS_SINGLE_BIT: + fndecl = builtin_decl_explicit (BUILT_IN_POPCOUNTG); + break; + default: + gcc_unreachable (); + } + /* Construct a call to __builtin_{clz,ctz,popcount}g. */ + int nargs = barg2 != NULL_TREE ? 2 : 1; + vec<tree, va_gc> *args; + vec_alloc (args, nargs); + vec<tree, va_gc> *origtypes; + vec_alloc (origtypes, nargs); + auto_vec<location_t> arg_loc (nargs); + args->quick_push (barg1); + arg_loc.quick_push (arg_p->get_location ()); + origtypes->quick_push (arg_p->original_type); + if (nargs == 2) + { + args->quick_push (barg2); + arg_loc.quick_push (loc); + origtypes->quick_push (integer_type_node); + } + expr.value = c_build_function_call_vec (loc, arg_loc, fndecl, + args, origtypes); + set_c_expr_source_range (&expr, loc, close_paren_loc); + if (expr.value == error_mark_node) + break; + switch (stdc_rid) + { + case C_BUILTIN_STDC_BIT_CEIL: + case C_BUILTIN_STDC_BIT_FLOOR: + --prec; + /* FALLTHRU */ + case C_BUILTIN_STDC_BIT_WIDTH: + expr.value = build2_loc (loc, MINUS_EXPR, integer_type_node, + build_int_cst (integer_type_node, + prec), expr.value); + break; + case C_BUILTIN_STDC_FIRST_LEADING_ONE: + case C_BUILTIN_STDC_FIRST_LEADING_ZERO: + case C_BUILTIN_STDC_FIRST_TRAILING_ONE: + case C_BUILTIN_STDC_FIRST_TRAILING_ZERO: + expr.value = build2_loc (loc, PLUS_EXPR, integer_type_node, + expr.value, integer_one_node); + break; + case C_BUILTIN_STDC_HAS_SINGLE_BIT: + expr.value = build2_loc (loc, EQ_EXPR, boolean_type_node, + expr.value, integer_one_node); + break; + default: + break; + } + + if (stdc_rid != C_BUILTIN_STDC_BIT_CEIL + && stdc_rid != C_BUILTIN_STDC_BIT_FLOOR) + { + if (stdc_rid != C_BUILTIN_STDC_HAS_SINGLE_BIT) + expr.value = fold_convert_loc (loc, unsigned_type_node, + expr.value); + break; + } + /* For __builtin_stdc_bit_ceil (0U) or __builtin_stdc_bit_ceil (1U) + or __builtin_stdc_bit_floor (0U) avoid bogus -Wshift-count-* + warnings. The LSHIFT_EXPR is in dead code in that case. */ + if (integer_zerop (arg) + || (stdc_rid == C_BUILTIN_STDC_BIT_CEIL && integer_onep (arg))) + expr.value = build_int_cst (type, 0); + else + expr.value + = build2_loc (loc, LSHIFT_EXPR, type, + build_int_cst (type, + (stdc_rid + == C_BUILTIN_STDC_BIT_CEIL + ? 2 : 1)), expr.value); + if (stdc_rid == C_BUILTIN_STDC_BIT_CEIL) + expr.value = build3_loc (loc, COND_EXPR, type, + build2_loc (loc, LE_EXPR, + boolean_type_node, arg, + build_int_cst (type, 1)), + build_int_cst (type, 1), + expr.value); + else + expr.value = build3_loc (loc, COND_EXPR, type, + build2_loc (loc, EQ_EXPR, + boolean_type_node, arg, + build_int_cst (type, 0)), + build_int_cst (type, 0), + expr.value); + break; + } case RID_AT_SELECTOR: { gcc_assert (c_dialect_objc ()); |