aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2020-12-03 15:46:54 +0100
committerJakub Jelinek <jakub@redhat.com>2020-12-03 15:46:54 +0100
commit896048cf43d5eb21ab7c16553bb9d13b0f890b81 (patch)
tree3d929f74e3ad0cd70a86d101744be4a961a57450 /gcc/cp
parent341035a54aa8954c66b0492f366b8c65fdb34299 (diff)
downloadgcc-896048cf43d5eb21ab7c16553bb9d13b0f890b81.zip
gcc-896048cf43d5eb21ab7c16553bb9d13b0f890b81.tar.gz
gcc-896048cf43d5eb21ab7c16553bb9d13b0f890b81.tar.bz2
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 <jakub@redhat.com> 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.
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/constexpr.c206
-rw-r--r--gcc/cp/cp-gimplify.c5
-rw-r--r--gcc/cp/cp-objcp-common.c2
-rw-r--r--gcc/cp/cp-tree.def3
-rw-r--r--gcc/cp/cp-tree.h2
-rw-r--r--gcc/cp/cxx-pretty-print.c9
-rw-r--r--gcc/cp/parser.c26
-rw-r--r--gcc/cp/pt.c7
-rw-r--r--gcc/cp/semantics.c71
-rw-r--r--gcc/cp/tree.c2
10 files changed, 333 insertions, 0 deletions
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index cd34e8e..e0d3580 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<T>, is_pointer_v<T>,
+ is_member_pointer_v<T>, is_volatile_v<T> 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 84b8d16..bafcaf5 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 a38bb0a..7ff4d39 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 4e73e46..c58b233 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 d77ec11..de905dc 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 1cdf077..b97f70e 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 ef4d73d..103567c 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 aa050f5..66ac647 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 5ff70ff..d33ef42 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 4d9efb7..2d97c86 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));