2024-11-08 Jakub Jelinek <ja...@redhat.com>
gcc/c-family/
* c-common.h (enum rid): Add RID_BUILTIN_OPERATOR_NEW
and RID_BUILTIN_OPERATOR_DELETE.
(names_builtin_p): Change return type from bool to int.
* c-common.cc (c_common_reswords): Add __builtin_operator_new
and __builtin_operator_delete.
gcc/c/
* c-decl.cc (names_builtin_p): Change return type from
bool to int, adjust return statments.
gcc/cp/
* parser.cc (cp_parser_postfix_expression): Handle
RID_BUILTIN_OPERATOR_NEW and RID_BUILTIN_OPERATOR_DELETE.
* cp-objcp-common.cc (names_builtin_p): Change return type from
bool to int, adjust return statments. Handle
RID_BUILTIN_OPERATOR_NEW and RID_BUILTIN_OPERATOR_DELETE.
* pt.cc (tsubst_expr) <case CALL_EXPR>: Handle
CALL_FROM_NEW_OR_DELETE_P.
gcc/
* doc/extend.texi (New/Delete Builtins): Document
__builtin_operator_new and __builtin_operator_delete.
gcc/testsuite/
* g++.dg/ext/builtin-operator-new-1.C: New test.
* g++.dg/ext/builtin-operator-new-2.C: New test.
* g++.dg/ext/builtin-operator-new-3.C: New test.
--- gcc/c-family/c-common.h.jj 2024-10-25 10:00:29.314770060 +0200
+++ gcc/c-family/c-common.h 2024-11-08 16:53:01.198538630 +0100
@@ -168,6 +168,7 @@ enum rid
RID_ADDRESSOF,
RID_BUILTIN_LAUNDER,
RID_BUILTIN_BIT_CAST,
+ RID_BUILTIN_OPERATOR_NEW, RID_BUILTIN_OPERATOR_DELETE,
/* C++11 */
RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT,
@@ -840,7 +841,7 @@ extern bool in_late_binary_op;
extern const char *c_addr_space_name (addr_space_t as);
extern tree identifier_global_value (tree);
extern tree identifier_global_tag (tree);
-extern bool names_builtin_p (const char *);
+extern int names_builtin_p (const char *);
extern tree c_linkage_bindings (tree);
extern void record_builtin_type (enum rid, const char *, tree);
extern void start_fname_decls (void);
--- gcc/c-family/c-common.cc.jj 2024-10-30 07:59:36.347587874 +0100
+++ gcc/c-family/c-common.cc 2024-11-08 15:33:51.733170328 +0100
@@ -434,6 +434,8 @@ const struct c_common_resword c_common_r
{ "__builtin_counted_by_ref", RID_BUILTIN_COUNTED_BY_REF, D_CONLY },
{ "__builtin_has_attribute", RID_BUILTIN_HAS_ATTRIBUTE, 0 },
{ "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY },
+ { "__builtin_operator_new", RID_BUILTIN_OPERATOR_NEW, D_CXXONLY },
+ { "__builtin_operator_delete", RID_BUILTIN_OPERATOR_DELETE, D_CXXONLY },
{ "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 },
{ "__builtin_shufflevector", RID_BUILTIN_SHUFFLEVECTOR, 0 },
{ "__builtin_stdc_bit_ceil", RID_BUILTIN_STDC, D_CONLY },
--- gcc/c/c-decl.cc.jj 2024-10-31 21:17:06.857021211 +0100
+++ gcc/c/c-decl.cc 2024-11-08 16:56:56.153195293 +0100
@@ -11751,10 +11751,10 @@ identifier_global_tag (tree t)
return NULL_TREE;
}
-/* Returns true if NAME refers to a built-in function or function-like
- operator. */
+/* Returns non-zero (result of __has_builtin) if NAME refers to a built-in
+ function or function-like operator. */
-bool
+int
names_builtin_p (const char *name)
{
tree id = get_identifier (name);
@@ -11775,12 +11775,12 @@ names_builtin_p (const char *name)
case RID_CHOOSE_EXPR:
case RID_OFFSETOF:
case RID_TYPES_COMPATIBLE_P:
- return true;
+ return 1;
default:
break;
}
- return false;
+ return 0;
}
/* In C, the only C-linkage public declaration is at file scope. */
--- gcc/cp/parser.cc.jj 2024-11-06 18:53:10.815844090 +0100
+++ gcc/cp/parser.cc 2024-11-08 17:52:04.332208389 +0100
@@ -7733,6 +7733,8 @@ cp_parser_postfix_expression (cp_parser
case RID_BUILTIN_SHUFFLEVECTOR:
case RID_BUILTIN_LAUNDER:
case RID_BUILTIN_ASSOC_BARRIER:
+ case RID_BUILTIN_OPERATOR_NEW:
+ case RID_BUILTIN_OPERATOR_DELETE:
{
vec<tree, va_gc> *vec;
@@ -7819,6 +7821,39 @@ cp_parser_postfix_expression (cp_parser
}
break;
+ case RID_BUILTIN_OPERATOR_NEW:
+ case RID_BUILTIN_OPERATOR_DELETE:
+ tree fn;
+ fn = ovl_op_identifier (keyword == RID_BUILTIN_OPERATOR_NEW
+ ? NEW_EXPR : DELETE_EXPR);
+ fn = lookup_qualified_name (global_namespace, fn);
+ postfix_expression = finish_call_expr (fn, &vec, true, false,
+ tf_warning_or_error);
+ if (postfix_expression != error_mark_node)
+ {
+ tree call = extract_call_expr (postfix_expression);
+ fn = cp_get_callee_fndecl_nofold (call);
+ if (fn ? !DECL_IS_REPLACEABLE_OPERATOR (fn)
+ : !processing_template_decl)
+ {
+ auto_diagnostic_group d;
+ if (keyword == RID_BUILTIN_OPERATOR_NEW)
+ error_at (loc, "call to %<__builtin_operator_new%> "
+ "does not select replaceable global "
+ "allocation function");
+ else
+ error_at (loc, "call to %<__builtin_operator_delete%> "
+ "does not select replaceable global "
+ "deallocation function");
+ if (fn)
+ inform (DECL_SOURCE_LOCATION (fn),
+ "selected function declared here");
+ }
+ else if (call && TREE_CODE (call) == CALL_EXPR)
+ CALL_FROM_NEW_OR_DELETE_P (call) = 1;
+ }
+ break;
+
default:
gcc_unreachable ();
}
--- gcc/cp/cp-objcp-common.cc.jj 2024-10-24 18:53:38.326085876 +0200
+++ gcc/cp/cp-objcp-common.cc 2024-11-08 16:57:52.061399744 +0100
@@ -545,10 +545,10 @@ identifier_global_tag (tree name)
return ret;
}
-/* Returns true if NAME refers to a built-in function or function-like
- operator. */
+/* Returns non-zero (result of __has_builtin) if NAME refers to a built-in
+ function or function-like operator. */
-bool
+int
names_builtin_p (const char *name)
{
tree id = get_identifier (name);
@@ -556,23 +556,23 @@ names_builtin_p (const char *name)
{
if (TREE_CODE (binding) == FUNCTION_DECL
&& DECL_IS_UNDECLARED_BUILTIN (binding))
- return true;
+ return 1;
/* Handle the case when an overload for a built-in name exists. */
if (TREE_CODE (binding) != OVERLOAD)
- return false;
+ return 0;
for (ovl_iterator it (binding); it; ++it)
{
tree decl = *it;
if (DECL_IS_UNDECLARED_BUILTIN (decl))
- return true;
+ return 1;
}
}
/* Check for built-in traits. */
if (IDENTIFIER_TRAIT_P (id))
- return true;
+ return 1;
/* Also detect common reserved C++ words that aren't strictly built-in
functions. */
@@ -587,12 +587,15 @@ names_builtin_p (const char *name)
case RID_BUILTIN_ASSOC_BARRIER:
case RID_BUILTIN_BIT_CAST:
case RID_OFFSETOF:
- return true;
+ return 1;
+ case RID_BUILTIN_OPERATOR_NEW:
+ case RID_BUILTIN_OPERATOR_DELETE:
+ return 201802L;
default:
break;
}
- return false;
+ return 0;
}
/* Register c++-specific dumps. */
--- gcc/cp/pt.cc.jj 2024-11-06 18:53:10.819844033 +0100
+++ gcc/cp/pt.cc 2024-11-08 18:11:01.996073922 +0100
@@ -21242,6 +21242,30 @@ tsubst_expr (tree t, tree args, tsubst_f
else if (TREE_CODE (call) == AGGR_INIT_EXPR)
AGGR_INIT_EXPR_MUST_TAIL (call) = mtc;
}
+ if (CALL_FROM_NEW_OR_DELETE_P (t))
+ {
+ tree call = extract_call_expr (ret);
+ tree fn = cp_get_callee_fndecl_nofold (call);
+ if (fn ? !DECL_IS_REPLACEABLE_OPERATOR (fn)
+ : !processing_template_decl)
+ {
+ auto_diagnostic_group d;
+ location_t loc = cp_expr_loc_or_input_loc (t);
+ if (!fn || IDENTIFIER_NEW_OP_P (DECL_NAME (fn)))
+ error_at (loc, "call to %<__builtin_operator_new%> "
+ "does not select replaceable global "
+ "allocation function");
+ else
+ error_at (loc, "call to %<__builtin_operator_delete%> "
+ "does not select replaceable global "
+ "deallocation function");
+ if (fn)
+ inform (DECL_SOURCE_LOCATION (fn),
+ "selected function declared here");
+ }
+ else if (call && TREE_CODE (call) == CALL_EXPR)
+ CALL_FROM_NEW_OR_DELETE_P (call) = 1;
+ }
if (warning_suppressed_p (t, OPT_Wpessimizing_move))
/* This also suppresses -Wredundant-move. */
suppress_warning (ret, OPT_Wpessimizing_move);
--- gcc/doc/extend.texi.jj 2024-11-07 13:16:47.863196448 +0100
+++ gcc/doc/extend.texi 2024-11-08 17:30:47.677330935 +0100
@@ -90,6 +90,7 @@ extensions, accepted by GCC in C90 mode
* x86 specific memory model extensions for transactional memory:: x86 memory
models.
* Object Size Checking:: Built-in functions for limited buffer overflow
checking.
+* New/Delete Builtins:: Built-in functions for C++ allocations and
deallocations.
* Other Builtins:: Other built-in functions.
* Target Builtins:: Built-in functions specific to particular targets.
* Target Format Checks:: Format checks specific to particular targets.
@@ -14313,6 +14314,33 @@ format string @var{fmt}. If the compile
is called and the @var{flag} argument passed to it.
@enddefbuiltin
+@node New/Delete Builtins
+@section Built-in functions for C++ allocations and deallocations
+@findex __builtin_operator_new
+@findex __builtin_operator_delete
+Calling these C++ built-in functions is similar to calling
+@code{::operator new} or @code{::operator delete} with the same arguments,
+except that it is an error if the selected @code{::operator new} or
+@code{::operator delete} overload is not a replaceable global operator
+and for optimization purposes calls to pairs of these functions can be
+omitted if access to the allocation is optimized out, or could be replaced
+with implementation provided buffer on the stack, or multiple allocation
+calls can be merged into a single allocation. In C++ such optimizations
+are normally allowed just for calls to such replaceable global operators
+from @code{new} and @code{delete} expressions.
+
+@smallexample
+void foo () @{
+ int *a = new int;
+ delete a; // This pair of allocation/deallocation operators can be omitted
+ // or replaced with int _temp; int *a = &_temp; etc.@:
+ void *b = ::operator new (32);
+ ::operator delete (b); // This one cannnot.
+ void *c = __builtin_operator_new (32);
+ __builtin_operator_delete (c); // This one can.
+@}
+@end smallexample
+
@node Other Builtins
@section Other Built-in Functions Provided by GCC
@cindex built-in functions
--- gcc/testsuite/g++.dg/ext/builtin-operator-new-1.C.jj 2024-11-08
16:34:52.905011446 +0100
+++ gcc/testsuite/g++.dg/ext/builtin-operator-new-1.C 2024-11-08
17:38:43.996566453 +0100
@@ -0,0 +1,106 @@
+// { dg-do compile }
+// { dg-options "-O2 -fdump-tree-gimple -fdump-tree-optimized" }
+// { dg-final { scan-tree-dump " = operator new \\\(" "gimple" } }
+// { dg-final { scan-tree-dump "operator delete \\\(" "gimple" } }
+// { dg-final { scan-tree-dump-not "operator new \\\(" "optimized" } }
+// { dg-final { scan-tree-dump-not "operator delete \\\(" "optimized" } }
+
+#include <new>
+
+#if __has_builtin (__builtin_operator_new) != 201802L
+#error "Unexpected value of __has_builtin (__builtin_operator_new)"
+#endif
+#if __has_builtin (__builtin_operator_delete) != 201802L
+#error "Unexpected value of __has_builtin (__builtin_operator_delete)"
+#endif
+
+void
+foo ()
+{
+ void *a = __builtin_operator_new (32);
+ __builtin_operator_delete (a);
+#if __cpp_sized_deallocation
+ a = __builtin_operator_new (32);
+ __builtin_operator_delete (a, 32);
+#endif
+#if __cpp_aligned_new
+ void *b = __builtin_operator_new (32, std::align_val_t(32));
+ __builtin_operator_delete (b, std::align_val_t(32));
+#if __cpp_sized_deallocation
+ b = __builtin_operator_new (32, std::align_val_t(32));
+ __builtin_operator_delete (b, 32, std::align_val_t(32));
+#endif
+#endif
+ void *c = __builtin_operator_new (32, std::nothrow);
+ __builtin_operator_delete (c, std::nothrow);
+#if __cpp_aligned_new
+ void *d = __builtin_operator_new (32, std::align_val_t(32), std::nothrow);
+ __builtin_operator_delete (d, std::align_val_t(32), std::nothrow);
+#endif
+ void *e = __builtin_operator_new (1.f);
+ __builtin_operator_delete (e);
+}
+
+template <int N>
+void
+bar ()
+{
+ void *a = __builtin_operator_new (32);
+ __builtin_operator_delete (a);
+#if __cpp_sized_deallocation
+ a = __builtin_operator_new (32);
+ __builtin_operator_delete (a, 32);
+#endif
+#if __cpp_aligned_new
+ void *b = __builtin_operator_new (32, std::align_val_t(32));
+ __builtin_operator_delete (b, std::align_val_t(32));
+#if __cpp_sized_deallocation
+ b = __builtin_operator_new (32, std::align_val_t(32));
+ __builtin_operator_delete (b, 32, std::align_val_t(32));
+#endif
+#endif
+ void *c = __builtin_operator_new (32, std::nothrow);
+ __builtin_operator_delete (c, std::nothrow);
+#if __cpp_aligned_new
+ void *d = __builtin_operator_new (32, std::align_val_t(32), std::nothrow);
+ __builtin_operator_delete (d, std::align_val_t(32), std::nothrow);
+#endif
+ void *e = __builtin_operator_new (1.f);
+ __builtin_operator_delete (e);
+}
+
+template <typename T, typename U, typename V, typename W>
+void
+baz (T sz, V v, W w)
+{
+ U a = __builtin_operator_new (sz);
+ __builtin_operator_delete (a);
+#if __cpp_sized_deallocation
+ a = __builtin_operator_new (sz);
+ __builtin_operator_delete (a, sz);
+#endif
+#if __cpp_aligned_new
+ U b = __builtin_operator_new (sz, std::align_val_t(sz));
+ __builtin_operator_delete (b, std::align_val_t(sz));
+#if __cpp_sized_deallocation
+ b = __builtin_operator_new (sz, std::align_val_t(sz));
+ __builtin_operator_delete (b, sz, std::align_val_t(sz));
+#endif
+#endif
+ U c = __builtin_operator_new (sz, v);
+ __builtin_operator_delete (c, v);
+#if __cpp_aligned_new
+ U d = __builtin_operator_new (sz, std::align_val_t(sz), v);
+ __builtin_operator_delete (d, std::align_val_t(sz), v);
+#endif
+ U e = __builtin_operator_new (w);
+ __builtin_operator_delete (e);
+}
+
+void
+qux ()
+{
+ bar <0> ();
+ baz <std::size_t, void *, const std::nothrow_t &, float> (32, std::nothrow,
+ 1.f);
+}
--- gcc/testsuite/g++.dg/ext/builtin-operator-new-2.C.jj 2024-11-08
16:42:34.552451715 +0100
+++ gcc/testsuite/g++.dg/ext/builtin-operator-new-2.C 2024-11-08
18:06:47.469683258 +0100
@@ -0,0 +1,49 @@
+// { dg-do compile }
+
+#include <new>
+
+void *operator new (std::size_t, float, double); // { dg-message "selected
function declared here" }
+void operator delete (void *, float, double); // { dg-message "selected
function declared here" }
+
+void
+foo (void *x)
+{
+ void *a = __builtin_operator_new (32, 1.f, 32.); // { dg-error "call to
'__builtin_operator_new' does not select replaceable global allocation function" }
+ __builtin_operator_delete (a, 1.f, 32.); // { dg-error "call to
'__builtin_operator_delete' does not select replaceable global deallocation
function" }
+ void *b = __builtin_operator_new (32, x); // { dg-error "call to
'__builtin_operator_new' does not select replaceable global allocation function" }
+ void *c = __builtin_operator_new (32, 1LL, 1L, 1); // { dg-error "no matching
function for call to 'operator new\\\(int, long long int, long int, int\\\)'" }
+ __builtin_operator_delete (c, 1LL, 1L, 1); // { dg-error "no matching
function for call to 'operator delete\\\(void\\\*&, long long int, long int,
int\\\)'" }
+ void *d = __builtin_operator_new (); // { dg-error "no matching
function for call to 'operator new\\\(\\\)'" }
+ __builtin_operator_delete (); // { dg-error "no
matching function for call to 'operator delete\\\(\\\)'" }
+}
+
+template <int N>
+void
+bar (void *x)
+{
+ void *a = __builtin_operator_new (32, 1.f, 32.); // { dg-error "call to
'__builtin_operator_new' does not select replaceable global allocation function" }
+ __builtin_operator_delete (a, 1.f, 32.); // { dg-error "call to
'__builtin_operator_delete' does not select replaceable global deallocation
function" }
+ void *b = __builtin_operator_new (32, x); // { dg-error "call to
'__builtin_operator_new' does not select replaceable global allocation function" }
+ void *c = __builtin_operator_new (32, 1LL, 1L, 1); // { dg-error "no matching
function for call to 'operator new\\\(int, long long int, long int, int\\\)'" }
+ __builtin_operator_delete (c, 1LL, 1L, 1); // { dg-error "no matching
function for call to 'operator delete\\\(void\\\*&, long long int, long int,
int\\\)'" }
+ void *d = __builtin_operator_new (); // { dg-error "no matching
function for call to 'operator new\\\(\\\)'" }
+ __builtin_operator_delete (); // { dg-error "no
matching function for call to 'operator delete\\\(\\\)'" }
+}
+
+template <typename T, typename U, typename V>
+void
+baz (void *x, T sz, V v)
+{
+ U a = __builtin_operator_new (sz, 1.f, 32.); // { dg-error "call to
'__builtin_operator_new' does not select replaceable global allocation function" }
+ __builtin_operator_delete (a, 1.f, 32.); // { dg-error "call to
'__builtin_operator_delete' does not select replaceable global deallocation
function" }
+ U b = __builtin_operator_new (sz, x); // { dg-error "call
to '__builtin_operator_new' does not select replaceable global allocation function" }
+ U c = __builtin_operator_new (sz, v, 1L, 1); // { dg-error "no matching function
for call to 'operator new\\\(int&, long long int&, long int, int\\\)'" }
+ __builtin_operator_delete (c, v, 1L, 1); // { dg-error "no matching function
for call to 'operator delete\\\(void\\\*&, long long int&, long int, int\\\)'" }
+}
+
+void
+qux (void *x, void *y)
+{
+ bar <0> (x);
+ baz <int, void *, long long> (y, 32, 1LL);
+}
--- gcc/testsuite/g++.dg/ext/builtin-operator-new-3.C.jj 2024-11-08
18:12:07.583145144 +0100
+++ gcc/testsuite/g++.dg/ext/builtin-operator-new-3.C 2024-11-08
18:17:46.562357225 +0100
@@ -0,0 +1,47 @@
+// { dg-do run }
+
+#include <new>
+
+void
+use (void *p)
+{
+ int *volatile q = new (p) int;
+ *q = 42;
+ (*q)++;
+ if (*q != 43)
+ __builtin_abort ();
+}
+
+int
+main ()
+{
+ void *volatile a = __builtin_operator_new (sizeof (int));
+ use (a);
+ __builtin_operator_delete (a);
+#if __cpp_sized_deallocation
+ a = __builtin_operator_new (sizeof (int));
+ use (a);
+ __builtin_operator_delete (a, sizeof (int));
+#endif
+#if __cpp_aligned_new
+ void *volatile b = __builtin_operator_new (sizeof (int),
std::align_val_t(alignof (int) * 4));
+ use (b);
+ __builtin_operator_delete (b, std::align_val_t(alignof (int) * 4));
+#if __cpp_sized_deallocation
+ b = __builtin_operator_new (sizeof (int), std::align_val_t(alignof (int) *
4));
+ use (b);
+ __builtin_operator_delete (b, sizeof (int), std::align_val_t(alignof (int) *
4));
+#endif
+#endif
+ void *volatile c = __builtin_operator_new (sizeof (int), std::nothrow);
+ use (c);
+ __builtin_operator_delete (c, std::nothrow);
+#if __cpp_aligned_new
+ void *volatile d = __builtin_operator_new (sizeof (int),
std::align_val_t(alignof (int) * 4), std::nothrow);
+ use (d);
+ __builtin_operator_delete (d, std::align_val_t(sizeof (int)), std::nothrow);
+#endif
+ void *volatile e = __builtin_operator_new (1.f);
+ use (e);
+ __builtin_operator_delete (e);
+}
Jakub