From 896048cf43d5eb21ab7c16553bb9d13b0f890b81 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Thu, 3 Dec 2020 15:46:54 +0100 Subject: [PATCH] c++: Add __builtin_bit_cast to implement std::bit_cast [PR93121] The following patch adds __builtin_bit_cast builtin, similarly to clang or MSVC which implement std::bit_cast using such an builtin too. It checks the various std::bit_cast requirements, when not constexpr evaluated acts pretty much like VIEW_CONVERT_EXPR of the source argument to the destination type and the hardest part is obviously the constexpr evaluation. I've left out PDP11 handling of those, couldn't figure out how exactly are bitfields laid out there 2020-12-03 Jakub Jelinek PR libstdc++/93121 * fold-const.h (native_encode_initializer): Add mask argument defaulted to nullptr. (find_bitfield_repr_type): Declare. (native_interpret_aggregate): Declare. * fold-const.c (find_bitfield_repr_type): New function. (native_encode_initializer): Add mask argument and support for filling it. Handle also some bitfields without integral DECL_BIT_FIELD_REPRESENTATIVE. (native_interpret_aggregate): New function. * gimple-fold.h (clear_type_padding_in_mask): Declare. * gimple-fold.c (struct clear_padding_struct): Add clear_in_mask member. (clear_padding_flush): Handle buf->clear_in_mask. (clear_padding_union): Copy clear_in_mask. Don't error if buf->clear_in_mask is set. (clear_padding_type): Don't error if buf->clear_in_mask is set. (clear_type_padding_in_mask): New function. (gimple_fold_builtin_clear_padding): Set buf.clear_in_mask to false. * doc/extend.texi (__builtin_bit_cast): Document. * c-common.h (enum rid): Add RID_BUILTIN_BIT_CAST. * c-common.c (c_common_reswords): Add __builtin_bit_cast. * cp-tree.h (cp_build_bit_cast): Declare. * cp-tree.def (BIT_CAST_EXPR): New tree code. * cp-objcp-common.c (names_builtin_p): Handle RID_BUILTIN_BIT_CAST. (cp_common_init_ts): Handle BIT_CAST_EXPR. * cxx-pretty-print.c (cxx_pretty_printer::postfix_expression): Likewise. * parser.c (cp_parser_postfix_expression): Handle RID_BUILTIN_BIT_CAST. * semantics.c (cp_build_bit_cast): New function. * tree.c (cp_tree_equal): Handle BIT_CAST_EXPR. (cp_walk_subtrees): Likewise. * pt.c (tsubst_copy): Likewise. * constexpr.c (check_bit_cast_type, cxx_eval_bit_cast): New functions. (cxx_eval_constant_expression): Handle BIT_CAST_EXPR. (potential_constant_expression_1): Likewise. * cp-gimplify.c (cp_genericize_r): Likewise. * g++.dg/cpp2a/bit-cast1.C: New test. * g++.dg/cpp2a/bit-cast2.C: New test. * g++.dg/cpp2a/bit-cast3.C: New test. * g++.dg/cpp2a/bit-cast4.C: New test. * g++.dg/cpp2a/bit-cast5.C: New test. --- gcc/c-family/c-common.c | 1 + gcc/c-family/c-common.h | 2 +- gcc/cp/constexpr.c | 206 ++++++++++++ gcc/cp/cp-gimplify.c | 5 + gcc/cp/cp-objcp-common.c | 2 + gcc/cp/cp-tree.def | 3 + gcc/cp/cp-tree.h | 2 + gcc/cp/cxx-pretty-print.c | 9 + gcc/cp/parser.c | 26 ++ gcc/cp/pt.c | 7 + gcc/cp/semantics.c | 71 ++++ gcc/cp/tree.c | 2 + gcc/doc/extend.texi | 15 + gcc/fold-const.c | 440 ++++++++++++++++++++++--- gcc/fold-const.h | 4 +- gcc/gimple-fold.c | 74 ++++- gcc/gimple-fold.h | 1 + gcc/testsuite/g++.dg/cpp2a/bit-cast1.C | 47 +++ gcc/testsuite/g++.dg/cpp2a/bit-cast2.C | 57 ++++ gcc/testsuite/g++.dg/cpp2a/bit-cast3.C | 229 +++++++++++++ gcc/testsuite/g++.dg/cpp2a/bit-cast4.C | 44 +++ gcc/testsuite/g++.dg/cpp2a/bit-cast5.C | 69 ++++ 22 files changed, 1264 insertions(+), 52 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/bit-cast1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/bit-cast2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/bit-cast3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/bit-cast4.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/bit-cast5.C diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index cae883b5309..dda23520b96 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -374,6 +374,7 @@ const struct c_common_resword c_common_reswords[] = { "__auto_type", RID_AUTO_TYPE, D_CONLY }, { "__bases", RID_BASES, D_CXXONLY }, { "__builtin_addressof", RID_ADDRESSOF, D_CXXONLY }, + { "__builtin_bit_cast", RID_BUILTIN_BIT_CAST, D_CXXONLY }, { "__builtin_call_with_static_chain", RID_BUILTIN_CALL_WITH_STATIC_CHAIN, D_CONLY }, { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY }, diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 8c17067e63c..80830d812cc 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -169,7 +169,7 @@ enum rid RID_HAS_NOTHROW_COPY, RID_HAS_TRIVIAL_ASSIGN, RID_HAS_TRIVIAL_CONSTRUCTOR, RID_HAS_TRIVIAL_COPY, RID_HAS_TRIVIAL_DESTRUCTOR, RID_HAS_UNIQUE_OBJ_REPRESENTATIONS, - RID_HAS_VIRTUAL_DESTRUCTOR, + RID_HAS_VIRTUAL_DESTRUCTOR, RID_BUILTIN_BIT_CAST, RID_IS_ABSTRACT, RID_IS_AGGREGATE, RID_IS_BASE_OF, RID_IS_CLASS, RID_IS_EMPTY, RID_IS_ENUM, diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index cd34e8e7eb4..e0d358027c9 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -3950,6 +3950,205 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t, return error_mark_node; } +/* Helper for cxx_eval_bit_cast. + Check [bit.cast]/3 rules, bit_cast is constexpr only if the To and From + types and types of all subobjects have is_union_v, is_pointer_v, + is_member_pointer_v, is_volatile_v false and has no non-static + data members of reference type. */ + +static bool +check_bit_cast_type (const constexpr_ctx *ctx, location_t loc, tree type, + tree orig_type) +{ + if (TREE_CODE (type) == UNION_TYPE) + { + if (!ctx->quiet) + { + if (type == orig_type) + error_at (loc, "%qs is not a constant expression because %qT is " + "a union type", "__builtin_bit_cast", type); + else + error_at (loc, "%qs is not a constant expression because %qT " + "contains a union type", "__builtin_bit_cast", + orig_type); + } + return true; + } + if (TREE_CODE (type) == POINTER_TYPE) + { + if (!ctx->quiet) + { + if (type == orig_type) + error_at (loc, "%qs is not a constant expression because %qT is " + "a pointer type", "__builtin_bit_cast", type); + else + error_at (loc, "%qs is not a constant expression because %qT " + "contains a pointer type", "__builtin_bit_cast", + orig_type); + } + return true; + } + if (TREE_CODE (type) == REFERENCE_TYPE) + { + if (!ctx->quiet) + { + if (type == orig_type) + error_at (loc, "%qs is not a constant expression because %qT is " + "a reference type", "__builtin_bit_cast", type); + else + error_at (loc, "%qs is not a constant expression because %qT " + "contains a reference type", "__builtin_bit_cast", + orig_type); + } + return true; + } + if (TYPE_PTRMEM_P (type)) + { + if (!ctx->quiet) + { + if (type == orig_type) + error_at (loc, "%qs is not a constant expression because %qT is " + "a pointer to member type", "__builtin_bit_cast", + type); + else + error_at (loc, "%qs is not a constant expression because %qT " + "contains a pointer to member type", + "__builtin_bit_cast", orig_type); + } + return true; + } + if (TYPE_VOLATILE (type)) + { + if (!ctx->quiet) + { + if (type == orig_type) + error_at (loc, "%qs is not a constant expression because %qT is " + "volatile", "__builtin_bit_cast", type); + else + error_at (loc, "%qs is not a constant expression because %qT " + "contains a volatile subobject", + "__builtin_bit_cast", orig_type); + } + return true; + } + if (TREE_CODE (type) == RECORD_TYPE) + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && check_bit_cast_type (ctx, loc, TREE_TYPE (field), orig_type)) + return true; + return false; +} + +/* Subroutine of cxx_eval_constant_expression. + Attempt to evaluate a BIT_CAST_EXPR. */ + +static tree +cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p, + bool *overflow_p) +{ + if (check_bit_cast_type (ctx, EXPR_LOCATION (t), TREE_TYPE (t), + TREE_TYPE (t)) + || check_bit_cast_type (ctx, cp_expr_loc_or_loc (TREE_OPERAND (t, 0), + EXPR_LOCATION (t)), + TREE_TYPE (TREE_OPERAND (t, 0)), + TREE_TYPE (TREE_OPERAND (t, 0)))) + { + *non_constant_p = true; + return t; + } + + tree op = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), false, + non_constant_p, overflow_p); + if (*non_constant_p) + return t; + + location_t loc = EXPR_LOCATION (t); + if (BITS_PER_UNIT != 8 || CHAR_BIT != 8) + { + if (!ctx->quiet) + sorry_at (loc, "%qs cannot be constant evaluated on the target", + "__builtin_bit_cast"); + *non_constant_p = true; + return t; + } + + if (!tree_fits_shwi_p (TYPE_SIZE_UNIT (TREE_TYPE (t)))) + { + if (!ctx->quiet) + sorry_at (loc, "%qs cannot be constant evaluated because the " + "type is too large", "__builtin_bit_cast"); + *non_constant_p = true; + return t; + } + + HOST_WIDE_INT len = tree_to_shwi (TYPE_SIZE_UNIT (TREE_TYPE (t))); + if (len < 0 || (int) len != len) + { + if (!ctx->quiet) + sorry_at (loc, "%qs cannot be constant evaluated because the " + "type is too large", "__builtin_bit_cast"); + *non_constant_p = true; + return t; + } + + unsigned char buf[64]; + unsigned char *ptr, *mask; + size_t alen = (size_t) len * 2; + if (alen <= sizeof (buf)) + ptr = buf; + else + ptr = XNEWVEC (unsigned char, alen); + mask = ptr + (size_t) len; + /* At the beginning consider everything indeterminate. */ + memset (mask, ~0, (size_t) len); + + if (native_encode_initializer (op, ptr, len, 0, mask) != len) + { + if (!ctx->quiet) + sorry_at (loc, "%qs cannot be constant evaluated because the " + "argument cannot be encoded", "__builtin_bit_cast"); + *non_constant_p = true; + if (ptr != buf) + XDELETE (ptr); + return t; + } + + tree r = NULL_TREE; + if (can_native_interpret_type_p (TREE_TYPE (t))) + r = native_interpret_expr (TREE_TYPE (t), ptr, len); + else if (TREE_CODE (TREE_TYPE (t)) == RECORD_TYPE) + { + r = native_interpret_aggregate (TREE_TYPE (t), ptr, 0, len); + if (r != NULL_TREE) + clear_type_padding_in_mask (TREE_TYPE (t), mask); + } + + if (r != NULL_TREE) + { + for (int i = 0; i < len; i++) + if (mask[i]) + { + if (!ctx->quiet) + error_at (loc, "%qs accessing uninitialized byte at offset %d", + "__builtin_bit_cast", i); + *non_constant_p = true; + r = t; + break; + } + if (ptr != buf) + XDELETE (ptr); + return r; + } + + if (!ctx->quiet) + sorry_at (loc, "%qs cannot be constant evaluated because the " + "argument cannot be interpreted", "__builtin_bit_cast"); + *non_constant_p = true; + if (ptr != buf) + XDELETE (ptr); + return t; +} + /* Subroutine of cxx_eval_constant_expression. Evaluate a short-circuited logical expression T in the context of a given constexpr CALL. BAILOUT_VALUE is the value for @@ -6653,6 +6852,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, *non_constant_p = true; return t; + case BIT_CAST_EXPR: + r = cxx_eval_bit_cast (ctx, t, non_constant_p, overflow_p); + break; + default: if (STATEMENT_CODE_P (TREE_CODE (t))) { @@ -8442,6 +8645,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case ANNOTATE_EXPR: return RECUR (TREE_OPERAND (t, 0), rval); + case BIT_CAST_EXPR: + return RECUR (TREE_OPERAND (t, 0), rval); + /* Coroutine await, yield and return expressions are not. */ case CO_AWAIT_EXPR: case CO_YIELD_EXPR: diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index 84b8d16e742..bafcaf5369d 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -1560,6 +1560,11 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) cp_genericize_r, cp_walk_subtrees); break; + case BIT_CAST_EXPR: + *stmt_p = build1_loc (EXPR_LOCATION (stmt), VIEW_CONVERT_EXPR, + TREE_TYPE (stmt), TREE_OPERAND (stmt, 0)); + break; + default: if (IS_TYPE_OR_DECL_P (stmt)) *walk_subtrees = 0; diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c index a38bb0a0661..7ff4d3951b6 100644 --- a/gcc/cp/cp-objcp-common.c +++ b/gcc/cp/cp-objcp-common.c @@ -391,6 +391,7 @@ names_builtin_p (const char *name) case RID_BUILTIN_HAS_ATTRIBUTE: case RID_BUILTIN_SHUFFLE: case RID_BUILTIN_LAUNDER: + case RID_BUILTIN_BIT_CAST: case RID_OFFSETOF: case RID_HAS_NOTHROW_ASSIGN: case RID_HAS_NOTHROW_CONSTRUCTOR: @@ -489,6 +490,7 @@ cp_common_init_ts (void) MARK_TS_EXP (ALIGNOF_EXPR); MARK_TS_EXP (ARROW_EXPR); MARK_TS_EXP (AT_ENCODE_EXPR); + MARK_TS_EXP (BIT_CAST_EXPR); MARK_TS_EXP (CAST_EXPR); MARK_TS_EXP (CONST_CAST_EXPR); MARK_TS_EXP (CTOR_INITIALIZER); diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index 4e73e46b4a0..c58b2338379 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -440,6 +440,9 @@ DEFTREECODE (UNARY_RIGHT_FOLD_EXPR, "unary_right_fold_expr", tcc_expression, 2) DEFTREECODE (BINARY_LEFT_FOLD_EXPR, "binary_left_fold_expr", tcc_expression, 3) DEFTREECODE (BINARY_RIGHT_FOLD_EXPR, "binary_right_fold_expr", tcc_expression, 3) +/* Represents the __builtin_bit_cast (type, expr) expression. + The type is in TREE_TYPE, expression in TREE_OPERAND (bitcast, 0). */ +DEFTREECODE (BIT_CAST_EXPR, "bit_cast_expr", tcc_expression, 1) /** C++ extensions. */ diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index d77ec119be4..de905dcf37c 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7372,6 +7372,8 @@ extern tree finish_builtin_launder (location_t, tree, tsubst_flags_t); extern tree cp_build_vec_convert (tree, location_t, tree, tsubst_flags_t); +extern tree cp_build_bit_cast (location_t, tree, tree, + tsubst_flags_t); extern void start_lambda_scope (tree); extern void record_lambda_scope (tree); extern void record_null_lambda_scope (tree); diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c index 1cdf0772a6b..b97f70e2bd0 100644 --- a/gcc/cp/cxx-pretty-print.c +++ b/gcc/cp/cxx-pretty-print.c @@ -655,6 +655,15 @@ cxx_pretty_printer::postfix_expression (tree t) pp_right_paren (this); break; + case BIT_CAST_EXPR: + pp_cxx_ws_string (this, "__builtin_bit_cast"); + pp_left_paren (this); + type_id (TREE_TYPE (t)); + pp_comma (this); + expression (TREE_OPERAND (t, 0)); + pp_right_paren (this); + break; + case EMPTY_CLASS_EXPR: type_id (TREE_TYPE (t)); pp_left_paren (this); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index ef4d73d6161..103567cd004 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -7250,6 +7250,32 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p, tf_warning_or_error); } + case RID_BUILTIN_BIT_CAST: + { + tree expression; + tree type; + /* Consume the `__builtin_bit_cast' token. */ + cp_lexer_consume_token (parser->lexer); + /* Look for the opening `('. */ + matching_parens parens; + parens.require_open (parser); + location_t type_location + = cp_lexer_peek_token (parser->lexer)->location; + /* Parse the type-id. */ + { + type_id_in_expr_sentinel s (parser); + type = cp_parser_type_id (parser); + } + /* Look for the `,'. */ + cp_parser_require (parser, CPP_COMMA, RT_COMMA); + /* Now, parse the assignment-expression. */ + expression = cp_parser_assignment_expression (parser); + /* Look for the closing `)'. */ + parens.require_close (parser); + return cp_build_bit_cast (type_location, type, expression, + tf_warning_or_error); + } + default: { tree type; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index aa050f55fd6..66ac6473983 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -16731,6 +16731,13 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl) return build1 (code, type, op0); } + case BIT_CAST_EXPR: + { + tree type = tsubst (TREE_TYPE (t), args, complain, in_decl); + tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl); + return cp_build_bit_cast (EXPR_LOCATION (t), type, op0, complain); + } + case SIZEOF_EXPR: if (PACK_EXPANSION_P (TREE_OPERAND (t, 0)) || ARGUMENT_PACK_P (TREE_OPERAND (t, 0))) diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 5ff70ff4844..d33ef426348 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -10679,4 +10679,75 @@ cp_build_vec_convert (tree arg, location_t loc, tree type, return build_call_expr_internal_loc (loc, IFN_VEC_CONVERT, type, 1, arg); } +/* Finish __builtin_bit_cast (type, arg). */ + +tree +cp_build_bit_cast (location_t loc, tree type, tree arg, + tsubst_flags_t complain) +{ + if (error_operand_p (type)) + return error_mark_node; + if (!dependent_type_p (type)) + { + if (!complete_type_or_maybe_complain (type, NULL_TREE, complain)) + return error_mark_node; + if (TREE_CODE (type) == ARRAY_TYPE) + { + /* std::bit_cast for destination ARRAY_TYPE is not possible, + as functions may not return an array, so don't bother trying + to support this (and then deal with VLAs etc.). */ + error_at (loc, "%<__builtin_bit_cast%> destination type %qT " + "is an array type", type); + return error_mark_node; + } + if (!trivially_copyable_p (type)) + { + error_at (loc, "%<__builtin_bit_cast%> destination type %qT " + "is not trivially copyable", type); + return error_mark_node; + } + } + + if (error_operand_p (arg)) + return error_mark_node; + + if (!type_dependent_expression_p (arg)) + { + if (TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE) + { + /* Don't perform array-to-pointer conversion. */ + arg = mark_rvalue_use (arg, loc, true); + if (!complete_type_or_maybe_complain (TREE_TYPE (arg), arg, complain)) + return error_mark_node; + } + else + arg = decay_conversion (arg, complain); + + if (error_operand_p (arg)) + return error_mark_node; + + if (!trivially_copyable_p (TREE_TYPE (arg))) + { + error_at (cp_expr_loc_or_loc (arg, loc), + "%<__builtin_bit_cast%> source type %qT " + "is not trivially copyable", TREE_TYPE (arg)); + return error_mark_node; + } + if (!dependent_type_p (type) + && !cp_tree_equal (TYPE_SIZE_UNIT (type), + TYPE_SIZE_UNIT (TREE_TYPE (arg)))) + { + error_at (loc, "%<__builtin_bit_cast%> source size %qE " + "not equal to destination type size %qE", + TYPE_SIZE_UNIT (TREE_TYPE (arg)), + TYPE_SIZE_UNIT (type)); + return error_mark_node; + } + } + + tree ret = build_min (BIT_CAST_EXPR, type, arg); + SET_EXPR_LOCATION (ret, loc); + return ret; +} + #include "gt-cp-semantics.h" diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 4d9efb74744..2d97c86f83a 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -3969,6 +3969,7 @@ cp_tree_equal (tree t1, tree t2) CASE_CONVERT: case NON_LVALUE_EXPR: case VIEW_CONVERT_EXPR: + case BIT_CAST_EXPR: if (!same_type_p (TREE_TYPE (t1), TREE_TYPE (t2))) return false; /* Now compare operands as usual. */ @@ -5214,6 +5215,7 @@ cp_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, case CONST_CAST_EXPR: case DYNAMIC_CAST_EXPR: case IMPLICIT_CONV_EXPR: + case BIT_CAST_EXPR: if (TREE_TYPE (*tp)) WALK_SUBTREE (TREE_TYPE (*tp)); diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 23ede966bae..195bb21068a 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -13574,6 +13574,21 @@ have intederminate values and the object representation needs to be bitwise compared to some other object, for example for atomic operations. @end deftypefn +@deftypefn {Built-in Function} @var{type} __builtin_bit_cast (@var{type}, @var{arg}) +The @code{__builtin_bit_cast} function is available only +in C++. The built-in is intended to be used by implementations of +the @code{std::bit_cast} C++ template function. Programs should make +use of the latter function rather than invoking the built-in directly. + +This built-in function allows reinterpreting the bits of the @var{arg} +argument as if it had type @var{type}. @var{type} and the type of the +@var{arg} argument need to be trivially copyable types with the same size. +When manifestly constant-evaluated, it performs extra diagnostics required +for @code{std::bit_cast} and returns a constant expression if @var{arg} +is a constant expression. For more details +refer to the latest revision of the C++ standard. +@end deftypefn + @deftypefn {Built-in Function} long __builtin_expect (long @var{exp}, long @var{c}) @opindex fprofile-arcs You may use @code{__builtin_expect} to provide the compiler with diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 632a241a964..e77d74e40b0 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -7968,25 +7968,78 @@ native_encode_expr (const_tree expr, unsigned char *ptr, int len, int off) } } +/* Try to find a type whose byte size is smaller or equal to LEN bytes larger + or equal to FIELDSIZE bytes, with underlying mode precision/size multiple + of BITS_PER_UNIT. As native_{interpret,encode}_int works in term of + machine modes, we can't just use build_nonstandard_integer_type. */ + +tree +find_bitfield_repr_type (int fieldsize, int len) +{ + machine_mode mode; + for (int pass = 0; pass < 2; pass++) + { + enum mode_class mclass = pass ? MODE_PARTIAL_INT : MODE_INT; + FOR_EACH_MODE_IN_CLASS (mode, mclass) + if (known_ge (GET_MODE_SIZE (mode), fieldsize) + && known_eq (GET_MODE_PRECISION (mode), + GET_MODE_BITSIZE (mode)) + && known_le (GET_MODE_SIZE (mode), len)) + { + tree ret = lang_hooks.types.type_for_mode (mode, 1); + if (ret && TYPE_MODE (ret) == mode) + return ret; + } + } + + for (int i = 0; i < NUM_INT_N_ENTS; i ++) + if (int_n_enabled_p[i] + && int_n_data[i].bitsize >= (unsigned) (BITS_PER_UNIT * fieldsize) + && int_n_trees[i].unsigned_type) + { + tree ret = int_n_trees[i].unsigned_type; + mode = TYPE_MODE (ret); + if (known_ge (GET_MODE_SIZE (mode), fieldsize) + && known_eq (GET_MODE_PRECISION (mode), + GET_MODE_BITSIZE (mode)) + && known_le (GET_MODE_SIZE (mode), len)) + return ret; + } + + return NULL_TREE; +} + /* Similar to native_encode_expr, but also handle CONSTRUCTORs, VCEs, - NON_LVALUE_EXPRs and nops. */ + NON_LVALUE_EXPRs and nops. If MASK is non-NULL (then PTR has + to be non-NULL and OFF zero), then in addition to filling the + bytes pointed by PTR with the value also clear any bits pointed + by MASK that are known to be initialized, keep them as is for + e.g. uninitialized padding bits or uninitialized fields. */ int native_encode_initializer (tree init, unsigned char *ptr, int len, - int off) + int off, unsigned char *mask) { + int r; + /* We don't support starting at negative offset and -1 is special. */ if (off < -1 || init == NULL_TREE) return 0; + gcc_assert (mask == NULL || (off == 0 && ptr)); + STRIP_NOPS (init); switch (TREE_CODE (init)) { case VIEW_CONVERT_EXPR: case NON_LVALUE_EXPR: - return native_encode_initializer (TREE_OPERAND (init, 0), ptr, len, off); + return native_encode_initializer (TREE_OPERAND (init, 0), ptr, len, off, + mask); default: - return native_encode_expr (init, ptr, len, off); + r = native_encode_expr (init, ptr, len, off); + if (mask) + memset (mask, 0, r); + return r; case CONSTRUCTOR: tree type = TREE_TYPE (init); HOST_WIDE_INT total_bytes = int_size_in_bytes (type); @@ -7999,7 +8052,7 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, { HOST_WIDE_INT min_index; unsigned HOST_WIDE_INT cnt; - HOST_WIDE_INT curpos = 0, fieldsize; + HOST_WIDE_INT curpos = 0, fieldsize, valueinit = -1; constructor_elt *ce; if (TYPE_DOMAIN (type) == NULL_TREE @@ -8014,12 +8067,22 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, if (ptr != NULL) memset (ptr, '\0', MIN (total_bytes - off, len)); - FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce) + for (cnt = 0; ; cnt++) { - tree val = ce->value; - tree index = ce->index; + tree val = NULL_TREE, index = NULL_TREE; HOST_WIDE_INT pos = curpos, count = 0; bool full = false; + if (vec_safe_iterate (CONSTRUCTOR_ELTS (init), cnt, &ce)) + { + val = ce->value; + index = ce->index; + } + else if (mask == NULL + || CONSTRUCTOR_NO_CLEARING (init) + || curpos >= total_bytes) + break; + else + pos = total_bytes; if (index && TREE_CODE (index) == RANGE_EXPR) { if (!tree_fits_shwi_p (TREE_OPERAND (index, 0)) @@ -8037,6 +8100,28 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, pos = (tree_to_shwi (index) - min_index) * fieldsize; } + if (mask && !CONSTRUCTOR_NO_CLEARING (init) && curpos != pos) + { + if (valueinit == -1) + { + tree zero = build_constructor (TREE_TYPE (type), NULL); + r = native_encode_initializer (zero, ptr + curpos, + fieldsize, 0, + mask + curpos); + ggc_free (zero); + if (!r) + return 0; + valueinit = curpos; + curpos += fieldsize; + } + while (curpos != pos) + { + memcpy (ptr + curpos, ptr + valueinit, fieldsize); + memcpy (mask + curpos, mask + valueinit, fieldsize); + curpos += fieldsize; + } + } + curpos = pos; if (val) do @@ -8051,6 +8136,8 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, if (ptr) memcpy (ptr + (curpos - o), ptr + (pos - o), fieldsize); + if (mask) + memcpy (mask + curpos, mask + pos, fieldsize); } else if (!native_encode_initializer (val, ptr @@ -8058,7 +8145,10 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, : NULL, fieldsize, off == -1 ? -1 - : 0)) + : 0, + mask + ? mask + curpos + : NULL)) return 0; else { @@ -8073,6 +8163,7 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, unsigned char *p = NULL; int no = 0; int l; + gcc_assert (mask == NULL); if (curpos >= off) { if (ptr) @@ -8086,7 +8177,7 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, no = off - curpos; l = len; } - if (!native_encode_initializer (val, p, l, no)) + if (!native_encode_initializer (val, p, l, no, NULL)) return 0; } curpos += fieldsize; @@ -8100,22 +8191,74 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, { unsigned HOST_WIDE_INT cnt; constructor_elt *ce; + tree fld_base = TYPE_FIELDS (type); + tree to_free = NULL_TREE; + gcc_assert (TREE_CODE (type) == RECORD_TYPE || mask == NULL); if (ptr != NULL) memset (ptr, '\0', MIN (total_bytes - off, len)); - FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (init), cnt, ce) + for (cnt = 0; ; cnt++) { - tree field = ce->index; - tree val = ce->value; - HOST_WIDE_INT pos, fieldsize; + tree val = NULL_TREE, field = NULL_TREE; + HOST_WIDE_INT pos = 0, fieldsize; unsigned HOST_WIDE_INT bpos = 0, epos = 0; - if (field == NULL_TREE) - return 0; + if (to_free) + { + ggc_free (to_free); + to_free = NULL_TREE; + } - pos = int_byte_position (field); - if (off != -1 && (HOST_WIDE_INT) off + len <= pos) - continue; + if (vec_safe_iterate (CONSTRUCTOR_ELTS (init), cnt, &ce)) + { + val = ce->value; + field = ce->index; + if (field == NULL_TREE) + return 0; + + pos = int_byte_position (field); + if (off != -1 && (HOST_WIDE_INT) off + len <= pos) + continue; + } + else if (mask == NULL + || CONSTRUCTOR_NO_CLEARING (init)) + break; + else + pos = total_bytes; + + if (mask && !CONSTRUCTOR_NO_CLEARING (init)) + { + tree fld; + for (fld = fld_base; fld; fld = DECL_CHAIN (fld)) + { + if (TREE_CODE (fld) != FIELD_DECL) + continue; + if (fld == field) + break; + if (DECL_PADDING_P (fld)) + continue; + if (DECL_SIZE_UNIT (fld) == NULL_TREE + || !tree_fits_shwi_p (DECL_SIZE_UNIT (fld))) + return 0; + if (integer_zerop (DECL_SIZE_UNIT (fld))) + continue; + break; + } + if (fld == NULL_TREE) + { + if (ce == NULL) + break; + return 0; + } + fld_base = DECL_CHAIN (fld); + if (fld != field) + { + cnt--; + field = fld; + val = build_constructor (TREE_TYPE (fld), NULL); + to_free = val; + } + } if (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE && TYPE_DOMAIN (TREE_TYPE (field)) @@ -8156,30 +8299,49 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN) return 0; - tree repr = DECL_BIT_FIELD_REPRESENTATIVE (field); - if (repr == NULL_TREE - || TREE_CODE (val) != INTEGER_CST - || !INTEGRAL_TYPE_P (TREE_TYPE (repr))) + if (TREE_CODE (val) != INTEGER_CST) return 0; - HOST_WIDE_INT rpos = int_byte_position (repr); + tree repr = DECL_BIT_FIELD_REPRESENTATIVE (field); + tree repr_type = NULL_TREE; + HOST_WIDE_INT rpos = 0; + if (repr && INTEGRAL_TYPE_P (TREE_TYPE (repr))) + { + rpos = int_byte_position (repr); + repr_type = TREE_TYPE (repr); + } + else + { + repr_type = find_bitfield_repr_type (fieldsize, len); + if (repr_type == NULL_TREE) + return 0; + HOST_WIDE_INT repr_size = int_size_in_bytes (repr_type); + gcc_assert (repr_size > 0 && repr_size <= len); + if (pos + repr_size <= len) + rpos = pos; + else + { + rpos = len - repr_size; + gcc_assert (rpos <= pos); + } + } + if (rpos > pos) return 0; - wide_int w = wi::to_wide (val, - TYPE_PRECISION (TREE_TYPE (repr))); - int diff = (TYPE_PRECISION (TREE_TYPE (repr)) + wide_int w = wi::to_wide (val, TYPE_PRECISION (repr_type)); + int diff = (TYPE_PRECISION (repr_type) - TYPE_PRECISION (TREE_TYPE (field))); HOST_WIDE_INT bitoff = (pos - rpos) * BITS_PER_UNIT + bpos; if (!BYTES_BIG_ENDIAN) w = wi::lshift (w, bitoff); else w = wi::lshift (w, diff - bitoff); - val = wide_int_to_tree (TREE_TYPE (repr), w); + val = wide_int_to_tree (repr_type, w); unsigned char buf[MAX_BITSIZE_MODE_ANY_INT / BITS_PER_UNIT + 1]; int l = native_encode_int (val, buf, sizeof buf, 0); - if (l * BITS_PER_UNIT != TYPE_PRECISION (TREE_TYPE (repr))) + if (l * BITS_PER_UNIT != TYPE_PRECISION (repr_type)) return 0; if (ptr == NULL) @@ -8192,15 +8354,31 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, { if (!BYTES_BIG_ENDIAN) { - int mask = (1 << bpos) - 1; - buf[pos - rpos] &= ~mask; - buf[pos - rpos] |= ptr[pos - o] & mask; + int msk = (1 << bpos) - 1; + buf[pos - rpos] &= ~msk; + buf[pos - rpos] |= ptr[pos - o] & msk; + if (mask) + { + if (fieldsize > 1 || epos == 0) + mask[pos] &= msk; + else + mask[pos] &= (msk | ~((1 << epos) - 1)); + } } else { - int mask = (1 << (BITS_PER_UNIT - bpos)) - 1; - buf[pos - rpos] &= mask; - buf[pos - rpos] |= ptr[pos - o] & ~mask; + int msk = (1 << (BITS_PER_UNIT - bpos)) - 1; + buf[pos - rpos] &= msk; + buf[pos - rpos] |= ptr[pos - o] & ~msk; + if (mask) + { + if (fieldsize > 1 || epos == 0) + mask[pos] &= ~msk; + else + mask[pos] &= (~msk + | ((1 << (BITS_PER_UNIT - epos)) + - 1)); + } } } /* If the bitfield does not end at byte boundary, handle @@ -8211,27 +8389,37 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, { if (!BYTES_BIG_ENDIAN) { - int mask = (1 << epos) - 1; - buf[pos - rpos + fieldsize - 1] &= mask; + int msk = (1 << epos) - 1; + buf[pos - rpos + fieldsize - 1] &= msk; buf[pos - rpos + fieldsize - 1] - |= ptr[pos + fieldsize - 1 - o] & ~mask; + |= ptr[pos + fieldsize - 1 - o] & ~msk; + if (mask && (fieldsize > 1 || bpos == 0)) + mask[pos + fieldsize - 1] &= ~msk; } else { - int mask = (1 << (BITS_PER_UNIT - epos)) - 1; - buf[pos - rpos + fieldsize - 1] &= ~mask; + int msk = (1 << (BITS_PER_UNIT - epos)) - 1; + buf[pos - rpos + fieldsize - 1] &= ~msk; buf[pos - rpos + fieldsize - 1] - |= ptr[pos + fieldsize - 1 - o] & mask; + |= ptr[pos + fieldsize - 1 - o] & msk; + if (mask && (fieldsize > 1 || bpos == 0)) + mask[pos + fieldsize - 1] &= msk; } } if (off == -1 || (pos >= off && (pos + fieldsize <= (HOST_WIDE_INT) off + len))) - memcpy (ptr + pos - o, buf + (pos - rpos), fieldsize); + { + memcpy (ptr + pos - o, buf + (pos - rpos), fieldsize); + if (mask && (fieldsize > (bpos != 0) + (epos != 0))) + memset (mask + pos + (bpos != 0), 0, + fieldsize - (bpos != 0) - (epos != 0)); + } else { /* Partial overlap. */ HOST_WIDE_INT fsz = fieldsize; + gcc_assert (mask == NULL); if (pos < off) { fsz -= (off - pos); @@ -8251,7 +8439,8 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, if (!native_encode_initializer (val, ptr ? ptr + pos - o : NULL, fieldsize, - off == -1 ? -1 : 0)) + off == -1 ? -1 : 0, + mask ? mask + pos : NULL)) return 0; } else @@ -8260,6 +8449,7 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, unsigned char *p = NULL; int no = 0; int l; + gcc_assert (mask == NULL); if (pos >= off) { if (ptr) @@ -8273,7 +8463,7 @@ native_encode_initializer (tree init, unsigned char *ptr, int len, no = off - pos; l = len; } - if (!native_encode_initializer (val, p, l, no)) + if (!native_encode_initializer (val, p, l, no, NULL)) return 0; } } @@ -8549,6 +8739,168 @@ can_native_interpret_type_p (tree type) } } +/* Attempt to interpret aggregate of TYPE from bytes encoded in target + byte order at PTR + OFF with LEN bytes. Does not handle unions. */ + +tree +native_interpret_aggregate (tree type, const unsigned char *ptr, int off, + int len) +{ + vec *elts = NULL; + if (TREE_CODE (type) == ARRAY_TYPE) + { + HOST_WIDE_INT eltsz = int_size_in_bytes (TREE_TYPE (type)); + if (eltsz < 0 || eltsz > len || TYPE_DOMAIN (type) == NULL_TREE) + return NULL_TREE; + + HOST_WIDE_INT cnt = 0; + if (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) + { + if (!tree_fits_shwi_p (TYPE_MAX_VALUE (TYPE_DOMAIN (type)))) + return NULL_TREE; + cnt = tree_to_shwi (TYPE_MAX_VALUE (TYPE_DOMAIN (type))) + 1; + } + if (eltsz == 0) + cnt = 0; + HOST_WIDE_INT pos = 0; + for (HOST_WIDE_INT i = 0; i < cnt; i++, pos += eltsz) + { + tree v = NULL_TREE; + if (pos >= len || pos + eltsz > len) + return NULL_TREE; + if (can_native_interpret_type_p (TREE_TYPE (type))) + { + v = native_interpret_expr (TREE_TYPE (type), + ptr + off + pos, eltsz); + if (v == NULL_TREE) + return NULL_TREE; + } + else if (TREE_CODE (TREE_TYPE (type)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE) + v = native_interpret_aggregate (TREE_TYPE (type), ptr, off + pos, + eltsz); + if (v == NULL_TREE) + return NULL_TREE; + CONSTRUCTOR_APPEND_ELT (elts, size_int (i), v); + } + return build_constructor (type, elts); + } + if (TREE_CODE (type) != RECORD_TYPE) + return NULL_TREE; + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) != FIELD_DECL || DECL_PADDING_P (field)) + continue; + tree fld = field; + HOST_WIDE_INT bitoff = 0, pos = 0, sz = 0; + int diff = 0; + tree v = NULL_TREE; + if (DECL_BIT_FIELD (field)) + { + fld = DECL_BIT_FIELD_REPRESENTATIVE (field); + if (fld && INTEGRAL_TYPE_P (TREE_TYPE (fld))) + { + poly_int64 bitoffset; + poly_uint64 field_offset, fld_offset; + if (poly_int_tree_p (DECL_FIELD_OFFSET (field), &field_offset) + && poly_int_tree_p (DECL_FIELD_OFFSET (fld), &fld_offset)) + bitoffset = (field_offset - fld_offset) * BITS_PER_UNIT; + else + bitoffset = 0; + bitoffset += (tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field)) + - tree_to_uhwi (DECL_FIELD_BIT_OFFSET (fld))); + diff = (TYPE_PRECISION (TREE_TYPE (fld)) + - TYPE_PRECISION (TREE_TYPE (field))); + if (!bitoffset.is_constant (&bitoff) + || bitoff < 0 + || bitoff > diff) + return NULL_TREE; + } + else + { + if (!tree_fits_uhwi_p (DECL_FIELD_BIT_OFFSET (field))) + return NULL_TREE; + int fieldsize = TYPE_PRECISION (TREE_TYPE (field)); + int bpos = tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field)); + bpos %= BITS_PER_UNIT; + fieldsize += bpos; + fieldsize += BITS_PER_UNIT - 1; + fieldsize /= BITS_PER_UNIT; + tree repr_type = find_bitfield_repr_type (fieldsize, len); + if (repr_type == NULL_TREE) + return NULL_TREE; + sz = int_size_in_bytes (repr_type); + if (sz < 0 || sz > len) + return NULL_TREE; + pos = int_byte_position (field); + if (pos < 0 || pos > len || pos + fieldsize > len) + return NULL_TREE; + HOST_WIDE_INT rpos; + if (pos + sz <= len) + rpos = pos; + else + { + rpos = len - sz; + gcc_assert (rpos <= pos); + } + bitoff = (HOST_WIDE_INT) (pos - rpos) * BITS_PER_UNIT + bpos; + pos = rpos; + diff = (TYPE_PRECISION (repr_type) + - TYPE_PRECISION (TREE_TYPE (field))); + v = native_interpret_expr (repr_type, ptr + off + pos, sz); + if (v == NULL_TREE) + return NULL_TREE; + fld = NULL_TREE; + } + } + + if (fld) + { + sz = int_size_in_bytes (TREE_TYPE (fld)); + if (sz < 0 || sz > len) + return NULL_TREE; + tree byte_pos = byte_position (fld); + if (!tree_fits_shwi_p (byte_pos)) + return NULL_TREE; + pos = tree_to_shwi (byte_pos); + if (pos < 0 || pos > len || pos + sz > len) + return NULL_TREE; + } + if (fld == NULL_TREE) + /* Already handled above. */; + else if (can_native_interpret_type_p (TREE_TYPE (fld))) + { + v = native_interpret_expr (TREE_TYPE (fld), + ptr + off + pos, sz); + if (v == NULL_TREE) + return NULL_TREE; + } + else if (TREE_CODE (TREE_TYPE (fld)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (fld)) == ARRAY_TYPE) + v = native_interpret_aggregate (TREE_TYPE (fld), ptr, off + pos, sz); + if (v == NULL_TREE) + return NULL_TREE; + if (fld != field) + { + if (TREE_CODE (v) != INTEGER_CST) + return NULL_TREE; + + /* FIXME: Figure out how to handle PDP endian bitfields. */ + if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN) + return NULL_TREE; + if (!BYTES_BIG_ENDIAN) + v = wide_int_to_tree (TREE_TYPE (field), + wi::lrshift (wi::to_wide (v), bitoff)); + else + v = wide_int_to_tree (TREE_TYPE (field), + wi::lrshift (wi::to_wide (v), + diff - bitoff)); + } + CONSTRUCTOR_APPEND_ELT (elts, field, v); + } + return build_constructor (type, elts); +} + /* Routines for manipulation of native_encode_expr encoded data if the encoded or extracted constant positions and/or sizes aren't byte aligned. */ diff --git a/gcc/fold-const.h b/gcc/fold-const.h index e833019475f..818c3d54a49 100644 --- a/gcc/fold-const.h +++ b/gcc/fold-const.h @@ -27,9 +27,11 @@ extern int folding_initializer; /* Convert between trees and native memory representation. */ extern int native_encode_expr (const_tree, unsigned char *, int, int off = -1); extern int native_encode_initializer (tree, unsigned char *, int, - int off = -1); + int off = -1, unsigned char * = nullptr); extern tree native_interpret_expr (tree, const unsigned char *, int); extern bool can_native_interpret_type_p (tree); +extern tree native_interpret_aggregate (tree, const unsigned char *, int, int); +extern tree find_bitfield_repr_type (int, int); extern void shift_bytes_in_array_left (unsigned char *, unsigned int, unsigned int); extern void shift_bytes_in_array_right (unsigned char *, unsigned int, diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index ab74494703a..3148c6b84d9 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -3958,6 +3958,10 @@ static const size_t clear_padding_buf_size = 32 * clear_padding_unit; /* Data passed through __builtin_clear_padding folding. */ struct clear_padding_struct { location_t loc; + /* 0 during __builtin_clear_padding folding, nonzero during + clear_type_padding_in_mask. In that case, instead of clearing the + non-padding bits in union_ptr array clear the padding bits in there. */ + bool clear_in_mask; tree base; tree alias_type; gimple_stmt_iterator *gsi; @@ -4000,6 +4004,39 @@ clear_padding_flush (clear_padding_struct *buf, bool full) size_t padding_bytes = buf->padding_bytes; if (buf->union_ptr) { + if (buf->clear_in_mask) + { + /* During clear_type_padding_in_mask, clear the padding + bits set in buf->buf in the buf->union_ptr mask. */ + for (size_t i = 0; i < end; i++) + { + if (buf->buf[i] == (unsigned char) ~0) + padding_bytes++; + else + { + memset (&buf->union_ptr[buf->off + i - padding_bytes], + 0, padding_bytes); + padding_bytes = 0; + buf->union_ptr[buf->off + i] &= ~buf->buf[i]; + } + } + if (full) + { + memset (&buf->union_ptr[buf->off + end - padding_bytes], + 0, padding_bytes); + buf->off = 0; + buf->size = 0; + buf->padding_bytes = 0; + } + else + { + memmove (buf->buf, buf->buf + end, buf->size - end); + buf->off += end; + buf->size -= end; + buf->padding_bytes = padding_bytes; + } + return; + } /* Inside of a union, instead of emitting any code, instead clear all bits in the union_ptr buffer that are clear in buf. Whole padding bytes don't clear anything. */ @@ -4311,6 +4348,7 @@ clear_padding_union (clear_padding_struct *buf, tree type, HOST_WIDE_INT sz) clear_padding_flush (buf, false); union_buf = XALLOCA (clear_padding_struct); union_buf->loc = buf->loc; + union_buf->clear_in_mask = buf->clear_in_mask; union_buf->base = NULL_TREE; union_buf->alias_type = NULL_TREE; union_buf->gsi = NULL; @@ -4335,9 +4373,10 @@ clear_padding_union (clear_padding_struct *buf, tree type, HOST_WIDE_INT sz) continue; gcc_assert (TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE && !COMPLETE_TYPE_P (TREE_TYPE (field))); - error_at (buf->loc, "flexible array member %qD does not have " - "well defined padding bits for %qs", - field, "__builtin_clear_padding"); + if (!buf->clear_in_mask) + error_at (buf->loc, "flexible array member %qD does not have " + "well defined padding bits for %qs", + field, "__builtin_clear_padding"); continue; } HOST_WIDE_INT fldsz = tree_to_shwi (DECL_SIZE_UNIT (field)); @@ -4529,9 +4568,10 @@ clear_padding_type (clear_padding_struct *buf, tree type, HOST_WIDE_INT sz) continue; gcc_assert (TREE_CODE (ftype) == ARRAY_TYPE && !COMPLETE_TYPE_P (ftype)); - error_at (buf->loc, "flexible array member %qD does not have " - "well defined padding bits for %qs", - field, "__builtin_clear_padding"); + if (!buf->clear_in_mask) + error_at (buf->loc, "flexible array member %qD does not " + "have well defined padding bits for %qs", + field, "__builtin_clear_padding"); } else if (is_empty_type (TREE_TYPE (field))) continue; @@ -4645,6 +4685,27 @@ clear_padding_type (clear_padding_struct *buf, tree type, HOST_WIDE_INT sz) } } +/* Clear padding bits of TYPE in MASK. */ + +void +clear_type_padding_in_mask (tree type, unsigned char *mask) +{ + clear_padding_struct buf; + buf.loc = UNKNOWN_LOCATION; + buf.clear_in_mask = true; + buf.base = NULL_TREE; + buf.alias_type = NULL_TREE; + buf.gsi = NULL; + buf.align = 0; + buf.off = 0; + buf.padding_bytes = 0; + buf.sz = int_size_in_bytes (type); + buf.size = 0; + buf.union_ptr = mask; + clear_padding_type (&buf, type, buf.sz); + clear_padding_flush (&buf, true); +} + /* Fold __builtin_clear_padding builtin. */ static bool @@ -4664,6 +4725,7 @@ gimple_fold_builtin_clear_padding (gimple_stmt_iterator *gsi) gsi_prev (&gsiprev); buf.loc = loc; + buf.clear_in_mask = false; buf.base = ptr; buf.alias_type = NULL_TREE; buf.gsi = gsi; diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h index a131e364f9a..f0f43f68dfa 100644 --- a/gcc/gimple-fold.h +++ b/gcc/gimple-fold.h @@ -35,6 +35,7 @@ extern tree maybe_fold_and_comparisons (tree, enum tree_code, tree, tree, enum tree_code, tree, tree); extern tree maybe_fold_or_comparisons (tree, enum tree_code, tree, tree, enum tree_code, tree, tree); +extern void clear_type_padding_in_mask (tree, unsigned char *); extern bool optimize_atomic_compare_exchange_p (gimple *); extern void fold_builtin_atomic_compare_exchange (gimple_stmt_iterator *); extern bool arith_overflowed_p (enum tree_code, const_tree, const_tree, diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast1.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast1.C new file mode 100644 index 00000000000..34cd1b93746 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast1.C @@ -0,0 +1,47 @@ +// { dg-do compile } + +struct S { short a, b; }; +struct T { float a[16]; }; +struct U { int b[16]; }; + +#if __SIZEOF_FLOAT__ == __SIZEOF_INT__ +int +f1 (float x) +{ + return __builtin_bit_cast (int, x); +} +#endif + +#if 2 * __SIZEOF_SHORT__ == __SIZEOF_INT__ +S +f2 (int x) +{ + return __builtin_bit_cast (S, x); +} + +int +f3 (S x) +{ + return __builtin_bit_cast (int, x); +} +#endif + +#if __SIZEOF_FLOAT__ == __SIZEOF_INT__ +U +f4 (T &x) +{ + return __builtin_bit_cast (U, x); +} + +T +f5 (int (&x)[16]) +{ + return __builtin_bit_cast (T, x); +} +#endif + +int +f6 () +{ + return __builtin_bit_cast (unsigned char, (signed char) 0); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C new file mode 100644 index 00000000000..6bb1760e621 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast2.C @@ -0,0 +1,57 @@ +// { dg-do compile } + +struct S { ~S (); int s; }; +S s; +struct V; // { dg-message "forward declaration of 'struct V'" } +extern V v; // { dg-error "'v' has incomplete type" } +extern V *p; +struct U { int a, b; }; +U u; + +void +foo (int *q) +{ + __builtin_bit_cast (int, s); // { dg-error "'__builtin_bit_cast' source type 'S' is not trivially copyable" } + __builtin_bit_cast (S, 0); // { dg-error "'__builtin_bit_cast' destination type 'S' is not trivially copyable" } + __builtin_bit_cast (int &, q); // { dg-error "'__builtin_bit_cast' destination type 'int&' is not trivially copyable" } + __builtin_bit_cast (int [1], 0); // { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is an array type" } + __builtin_bit_cast (V, 0); // { dg-error "invalid use of incomplete type 'struct V'" } + __builtin_bit_cast (int, v); + __builtin_bit_cast (int, *p); // { dg-error "invalid use of incomplete type 'struct V'" } + __builtin_bit_cast (U, 0); // { dg-error "'__builtin_bit_cast' source size '\[0-9]*' not equal to destination type size '\[0-9]*'" } + __builtin_bit_cast (int, u); // { dg-error "'__builtin_bit_cast' source size '\[0-9]*' not equal to destination type size '\[0-9]*'" } +} + +template +void +bar (int *q) +{ + __builtin_bit_cast (int, s); // { dg-error "'__builtin_bit_cast' source type 'S' is not trivially copyable" } + __builtin_bit_cast (S, 0); // { dg-error "'__builtin_bit_cast' destination type 'S' is not trivially copyable" } + __builtin_bit_cast (int &, q); // { dg-error "'__builtin_bit_cast' destination type 'int&' is not trivially copyable" } + __builtin_bit_cast (int [1], 0); // { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is an array type" } + __builtin_bit_cast (V, 0); // { dg-error "invalid use of incomplete type 'struct V'" } + __builtin_bit_cast (int, *p); // { dg-error "invalid use of incomplete type 'struct V'" } + __builtin_bit_cast (U, 0); // { dg-error "'__builtin_bit_cast' source size '\[0-9]*' not equal to destination type size '\[0-9]*'" } + __builtin_bit_cast (int, u); // { dg-error "'__builtin_bit_cast' source size '\[0-9]*' not equal to destination type size '\[0-9]*'" } +} + +template +void +baz (T3 s, T4 *p, T1 *q) +{ + __builtin_bit_cast (int, s); // { dg-error "'__builtin_bit_cast' source type 'S' is not trivially copyable" } + __builtin_bit_cast (T3, 0); // { dg-error "'__builtin_bit_cast' destination type 'S' is not trivially copyable" } + __builtin_bit_cast (T1 &, q); // { dg-error "'__builtin_bit_cast' destination type 'int&' is not trivially copyable" } + __builtin_bit_cast (T2, 0); // { dg-error "'__builtin_bit_cast' destination type \[^\n\r]* is an array type" } + __builtin_bit_cast (T4, 0); // { dg-error "invalid use of incomplete type 'struct V'" } + __builtin_bit_cast (int, *p); // { dg-error "invalid use of incomplete type 'struct V'" } + __builtin_bit_cast (U, (T1) 0); // { dg-error "'__builtin_bit_cast' source size '\[0-9]*' not equal to destination type size '\[0-9]*'" } + __builtin_bit_cast (T1, u); // { dg-error "'__builtin_bit_cast' source size '\[0-9]*' not equal to destination type size '\[0-9]*'" } +} + +void +qux (int *q) +{ + baz (s, p, q); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast3.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast3.C new file mode 100644 index 00000000000..a3ff211420d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast3.C @@ -0,0 +1,229 @@ +// { dg-do compile { target c++11 } } + +template +constexpr To +bit_cast (const From &from) +{ + return __builtin_bit_cast (To, from); +} + +template +constexpr bool +check (const From &from) +{ + return bit_cast (bit_cast (from)) == from; +} + +struct A +{ + int a, b, c; + constexpr bool operator == (const A &x) const + { + return x.a == a && x.b == b && x.c == c; + } +}; + +struct B +{ + unsigned a[3]; + constexpr bool operator == (const B &x) const + { + return x.a[0] == a[0] && x.a[1] == a[1] && x.a[2] == a[2]; + } +}; + +struct C +{ + char a[2][3][2]; + constexpr bool operator == (const C &x) const + { + return x.a[0][0][0] == a[0][0][0] + && x.a[0][0][1] == a[0][0][1] + && x.a[0][1][0] == a[0][1][0] + && x.a[0][1][1] == a[0][1][1] + && x.a[0][2][0] == a[0][2][0] + && x.a[0][2][1] == a[0][2][1] + && x.a[1][0][0] == a[1][0][0] + && x.a[1][0][1] == a[1][0][1] + && x.a[1][1][0] == a[1][1][0] + && x.a[1][1][1] == a[1][1][1] + && x.a[1][2][0] == a[1][2][0] + && x.a[1][2][1] == a[1][2][1]; + } +}; + +struct D +{ + int a, b; + constexpr bool operator == (const D &x) const + { + return x.a == a && x.b == b; + } +}; + +struct E {}; +struct F { char c, d, e, f; }; +struct G : public D, E, F +{ + int g; + constexpr bool operator == (const G &x) const + { + return x.a == a && x.b == b && x.c == c && x.d == d + && x.e == e && x.f == f && x.g == g; + } +}; + +struct H +{ + int a, b[2], c; + constexpr bool operator == (const H &x) const + { + return x.a == a && x.b[0] == b[0] && x.b[1] == b[1] && x.c == c; + } +}; + +#if __SIZEOF_INT__ == 4 +struct I +{ + int a; + int b : 3; + int c : 24; + int d : 5; + int e; + constexpr bool operator == (const I &x) const + { + return x.a == a && x.b == b && x.c == c && x.d == d && x.e == e; + } +}; +#endif + +#if __SIZEOF_INT__ == 4 && __SIZEOF_LONG_LONG__ == 8 +struct J +{ + long long int a, b : 11, c : 3, d : 37, e : 1, f : 10, g : 2, h; + constexpr bool operator == (const J &x) const + { + return x.a == a && x.b == b && x.c == c && x.d == d && x.e == e + && x.f == f && x.g == g && x.h == h; + } +}; + +struct K +{ + long long int a, b, c; + constexpr bool operator == (const K &x) const + { + return x.a == a && x.b == b && x.c == c; + } +}; + +struct M +{ + signed a : 6, b : 7, c : 6, d : 5; + unsigned char e; + unsigned int f; + long long int g; + constexpr bool operator == (const M &x) const + { + return x.a == a && x.b == b && x.c == c && x.d == d && x.e == e + && x.f == f && x.g == g; + } +}; + +struct N +{ + unsigned long long int a, b; + constexpr bool operator == (const N &x) const + { + return x.a == a && x.b == b; + } +}; +#endif + +static_assert (check (0), ""); +static_assert (check (0xdeadbeeffeedbac1ULL), ""); +static_assert (check ((unsigned char) 42), ""); +static_assert (check ((unsigned char) 42), ""); +static_assert (check ((unsigned char) 42), ""); +static_assert (check ((signed char) 42), ""); +static_assert (check ((signed char) 42), ""); +static_assert (check ((signed char) 42), ""); +static_assert (check ((char) 42), ""); +static_assert (check ((char) 42), ""); +static_assert (check ((char) 42), ""); +#if __SIZEOF_INT__ == __SIZEOF_FLOAT__ +static_assert (check (2.5f), ""); +static_assert (check (136.5f), ""); +#endif +#if __SIZEOF_LONG_LONG__ == __SIZEOF_DOUBLE__ +static_assert (check (2.5), ""); +static_assert (check (123456.75), ""); +#endif + +static_assert (check (A{ 1, 2, 3 }), ""); +static_assert (check (B{ 4, 5, 6 }), ""); + +#if __SIZEOF_INT__ == 4 +static_assert (check (A{ 7, 8, 9 }), ""); +static_assert (check (B{ 10, 11, 12 }), ""); +static_assert (check (C{ { { { 13, 14 }, { 15, 16 }, { 17, 18 } }, + { { 19, 20 }, { 21, 22 }, { 23, 24 } } } }), ""); +constexpr unsigned char c[] = { 1, 2, 3, 4 }; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +static_assert (bit_cast (c) == 0x04030201U, ""); +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +static_assert (bit_cast (c) == 0x01020304U, ""); +#endif + +#if __cplusplus >= 201703L +static_assert (check (H { 0x12345678, { 0x23456789, 0x5a876543 }, 0x3ba78654 }), ""); +#endif +constexpr int d[] = { 0x12345678, 0x23456789, 0x5a876543, 0x3ba78654 }; +static_assert (bit_cast (d) == bit_cast (H { 0x12345678, { 0x23456789, 0x5a876543 }, 0x3ba78654 }), ""); + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +static_assert (bit_cast (A { 0x7efa3412, 0x5a876543, 0x1eeffeed }) + == I { 0x7efa3412, 3, 0x50eca8, 0xb, 0x1eeffeed }, ""); +static_assert (bit_cast (I { 0x7efa3412, 3, 0x50eca8, 0xb, 0x1eeffeed }) + == A { 0x7efa3412, 0x5a876543, 0x1eeffeed }, ""); +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +static_assert (bit_cast (A { 0x7efa3412, 0x5a876543, 0x1eeffeed }) + == I { 0x7efa3412, 2, -0x2bc4d6, 0x3, 0x1eeffeed }, ""); +static_assert (bit_cast (I { 0x7efa3412, 2, -0x2bc4d6, 0x3, 0x1eeffeed }) + == A { 0x7efa3412, 0x5a876543, 0x1eeffeed }, ""); +#endif +#endif + +#if 2 * __SIZEOF_INT__ == __SIZEOF_LONG_LONG__ && __SIZEOF_INT__ >= 4 +constexpr unsigned long long a = 0xdeadbeeffee1deadULL; +constexpr unsigned b[] = { 0xfeedbacU, 0xbeeffeedU }; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +static_assert (bit_cast (a) == D { int (0xfee1deadU), int (0xdeadbeefU) }, ""); +static_assert (bit_cast (b) == 0xbeeffeed0feedbacULL, ""); +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +static_assert (bit_cast (a) == D { int (0xdeadbeefU), int (0xfee1deadU) }, ""); +static_assert (bit_cast (b) == 0x0feedbacbeeffeedULL, ""); +#endif +#endif + +#if __SIZEOF_INT__ == 4 && __SIZEOF_LONG_LONG__ == 8 +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +static_assert (bit_cast (K { 0x0feedbacdeadbeefLL, 7862463375103529997LL, 0x0feedbacdeadbeefLL }) + == J { 0x0feedbacdeadbeefLL, -1011, 2, -0xbacdeadbeLL, -1, -303, 1, 0x0feedbacdeadbeefLL }, ""); +static_assert (bit_cast (J { 0x0feedbacdeadbeefLL, -1011, 2, -0xbacdeadbeLL, -1, -303, 1, 0x0feedbacdeadbeefLL }) + == K { 0x0feedbacdeadbeefLL, 7862463375103529997LL, 0x0feedbacdeadbeefLL }, ""); +static_assert (bit_cast (N { 0xfeedbacdeadbeef8ULL, 0x123456789abcde42ULL }) + == M { -8, 59, 31, -5, 234, 0xfeedbacdU, 0x123456789abcde42ULL }, ""); +static_assert (bit_cast (M { -8, 59, 31, -5, 234, 0xfeedbacdU, 0x123456789abcde42ULL }) + == N { 0xfeedbacdeadbeef8ULL, 0x123456789abcde42ULL }, ""); +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +static_assert (bit_cast (K { 0x0feedbacdeadbeefLL, -9103311533965288635LL, 0x0feedbacdeadbeefLL }) + == J { 0x0feedbacdeadbeefLL, -1011, 2, -0xbacdeadbeLL, -1, -303, 1, 0x0feedbacdeadbeefLL }, ""); +static_assert (bit_cast (J { 0x0feedbacdeadbeefLL, -1011, 2, -0xbacdeadbeLL, -1, -303, 1, 0x0feedbacdeadbeefLL }) + == K { 0x0feedbacdeadbeefLL, -9103311533965288635LL, 0x0feedbacdeadbeefLL }, ""); +static_assert (bit_cast (N { 0xfeedbacdeadbeef8ULL, 0x123456789abcde42ULL }) + == M { -1, -35, -19, -6, 205, 0xeadbeef8U, 0x123456789abcde42ULL }, ""); +static_assert (bit_cast (M { -1, -35, -19, -6, 205, 0xeadbeef8U, 0x123456789abcde42ULL }) + == N { 0xfeedbacdeadbeef8ULL, 0x123456789abcde42ULL }, ""); +#endif +#endif diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast4.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast4.C new file mode 100644 index 00000000000..9ffa2ff01a2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast4.C @@ -0,0 +1,44 @@ +// { dg-do compile { target c++11 } } + +template +constexpr To +bit_cast (const From &from) +{ + return __builtin_bit_cast (To, from); +} +// { dg-error "'__builtin_bit_cast' is not a constant expression because 'U' is a union type" "U" { target *-*-* } 7 } +// { dg-error "'__builtin_bit_cast' is not a constant expression because 'const U' is a union type" "const U" { target *-*-* } 7 } +// { dg-error "'__builtin_bit_cast' is not a constant expression because 'B' contains a union type" "B" { target *-*-* } 7 } +// { dg-error "'__builtin_bit_cast' is not a constant expression because 'char\\\*' is a pointer type" "char ptr" { target *-*-* } 7 } +// { dg-error "'__builtin_bit_cast' is not a constant expression because 'const int\\\*' is a pointer type" "const int ptr" { target *-*-* } 7 } +// { dg-error "'__builtin_bit_cast' is not a constant expression because 'C' contains a pointer type" "C" { target *-*-* } 7 } +// { dg-error "'__builtin_bit_cast' is not a constant expression because 'const C' contains a pointer type" "const C" { target *-*-* } 7 } +// { dg-error "'__builtin_bit_cast' is not a constant expression because 'int D::\\\*' is a pointer to member type" "ptrmem 1" { target *-*-* } 7 } +// { dg-error "'__builtin_bit_cast' is not a constant expression because 'int \\\(D::\\\*\\\)\\\(\\\) const' is a pointer to member type" "ptrmem 2" { target *-*-* } 7 } +// { dg-error "'__builtin_bit_cast' is not a constant expression because 'int \\\(D::\\\*\\\)\\\(\\\)' is a pointer to member type" "ptrmem 3" { target *-*-* } 7 } + +union U { int u; }; +struct A { int a; U b; }; +struct B : public A { int c; }; +struct C { const int *p; }; +constexpr int a[] = { 1, 2, 3 }; +constexpr const int *b = &a[0]; +constexpr C c = { b }; +struct D { int d; constexpr int foo () const { return 1; } }; +constexpr int D::*d = &D::d; +constexpr int (D::*e) () const = &D::foo; +struct E { __INTPTR_TYPE__ e, f; }; +constexpr E f = { 1, 2 }; +constexpr U g { 0 }; + +constexpr auto z = bit_cast (0); +constexpr auto y = bit_cast (g); +constexpr auto x = bit_cast (a); +constexpr auto w = bit_cast ((__INTPTR_TYPE__) 0); +constexpr auto v = bit_cast <__UINTPTR_TYPE__> (b); +constexpr auto u = bit_cast ((__INTPTR_TYPE__) 0); +constexpr auto t = bit_cast <__INTPTR_TYPE__> (c); +constexpr auto s = bit_cast <__INTPTR_TYPE__> (d); +constexpr auto r = bit_cast (e); +constexpr auto q = bit_cast ((__INTPTR_TYPE__) 0); +constexpr auto p = bit_cast (f); diff --git a/gcc/testsuite/g++.dg/cpp2a/bit-cast5.C b/gcc/testsuite/g++.dg/cpp2a/bit-cast5.C new file mode 100644 index 00000000000..9d536d15984 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/bit-cast5.C @@ -0,0 +1,69 @@ +// { dg-do compile { target { c++20 && { ilp32 || lp64 } } } } + +struct A { signed char a, b, c, d, e, f; }; +struct B {}; +struct C { B a, b; short c; B d; }; +struct D { int a : 4, b : 24, c : 4; }; +struct E { B a, b; short c; }; +struct F { B a; signed char b, c; B d; }; + +constexpr bool +f1 () +{ + A a; + a.c = 23; a.d = 42; + C b = __builtin_bit_cast (C, a); // OK + return false; +} + +constexpr bool +f2 () +{ + A a; + a.a = 1; a.b = 2; a.c = 3; a.e = 4; a.f = 5; + C b = __builtin_bit_cast (C, a); // { dg-error "'__builtin_bit_cast' accessing uninitialized byte at offset 3" } + return false; +} + +constexpr bool +f3 () +{ + D a; + a.b = 1; + F b = __builtin_bit_cast (F, a); // OK + return false; +} + +constexpr bool +f4 () +{ + D a; + a.b = 1; a.c = 2; + E b = __builtin_bit_cast (E, a); // OK + return false; +} + +constexpr bool +f5 () +{ + D a; + a.b = 1; + E b = __builtin_bit_cast (E, a); // { dg-error "'__builtin_bit_cast' accessing uninitialized byte at offset 3" } + return false; +} + +constexpr bool +f6 () +{ + D a; + a.c = 1; + E b = __builtin_bit_cast (E, a); // { dg-error "'__builtin_bit_cast' accessing uninitialized byte at offset 2" } + return false; +} + +constexpr bool a = f1 (); +constexpr bool b = f2 (); +constexpr bool c = f3 (); +constexpr bool d = f4 (); +constexpr bool e = f5 (); +constexpr bool f = f6 (); -- 2.30.2