From: Nina Ranns <[email protected]>
This implements a no-ipa wrapper around the calls made from terminating
contract assertions so that callers can no longer make assuptions about
the no-return behaviour. This is sufficient to work around the reported
bug while a suitable general fix is evaluated.
gcc/c-family/ChangeLog:
* c.opt (fcontracts-conservative-ipa): New.
gcc/cp/ChangeLog:
* contracts.cc (__tu_terminate_wrapper): New.
(build_terminate_wrapper): New.
(get_terminate_wrapper): New.
(maybe_emit_violation_handler_wrappers): Build a no-ipa wrapper
for terminating contract violations if required.
Co-Authored-by: Iain Sandoe <[email protected]>
Signed-off-by: Iain Sandoe <[email protected]>
---
gcc/c-family/c.opt | 5 ++++
gcc/cp/contracts.cc | 59 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 64 insertions(+)
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 7ad7491b981..0ff9f14a459 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1919,6 +1919,11 @@ fcontract-disable-check-epochs
C++ ObjC++ Var(flag_contract_disable_check_epochs) Init(0)
-fcontract-disable-epoch-checks Disable insertion of epoch markers
after each contract check.
+fcontracts-conservative-ipa
+C++ ObjC++ Var(flag_contracts_conservative_ipa) Init(1)
+-fcontracts-conservative-ipa Do not allow certain optimisations between
+functions in contract assertions.
+
fcoroutines
C++ ObjC++ LTO Var(flag_coroutines)
Enable C++ coroutines (experimental).
diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc
index ac5af8faeaf..2d4466abff9 100644
--- a/gcc/cp/contracts.cc
+++ b/gcc/cp/contracts.cc
@@ -1453,6 +1453,63 @@ maybe_declare_violation_handler_wrappers ()
uint16_type_node);
}
+static GTY(()) tree __tu_terminate_wrapper = NULL_TREE;
+
+/* Define a noipa wrapper around the call to std::terminate */
+
+static void build_terminate_wrapper()
+{
+ start_preparsed_function (__tu_terminate_wrapper,
+ DECL_ATTRIBUTES(__tu_terminate_wrapper),
+ SF_DEFAULT | SF_PRE_PARSED);
+ tree body = begin_function_body ();
+ tree compound_stmt = begin_compound_stmt (BCS_FN_BODY);
+ finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr));
+ finish_return_stmt (NULL_TREE);
+ finish_compound_stmt (compound_stmt);
+ finish_function_body (body);
+ __tu_terminate_wrapper = finish_function (false);
+ expand_or_defer_fn (__tu_terminate_wrapper);
+}
+
+/* Get the decl for the noipa wrapper around the call to std::terminate */
+
+static tree
+get_terminate_wrapper ()
+{
+ if (__tu_terminate_wrapper)
+ return __tu_terminate_wrapper;
+
+ iloc_sentinel ils (input_location);
+ input_location = BUILTINS_LOCATION;
+
+ tree fn_type = build_function_type_list (void_type_node, NULL_TREE);
+ if (!TREE_NOTHROW (terminate_fn))
+ fn_type = build_exception_variant (fn_type, noexcept_true_spec);
+ tree fn_name = get_identifier ("__tu_terminate_wrapper");
+
+ __tu_terminate_wrapper
+ = build_lang_decl_loc (input_location, FUNCTION_DECL, fn_name, fn_type);
+ DECL_CONTEXT (__tu_terminate_wrapper) = FROB_CONTEXT(global_namespace);
+ DECL_ARTIFICIAL (__tu_terminate_wrapper) = true;
+ DECL_INITIAL (__tu_terminate_wrapper) = error_mark_node;
+ /* Let the start function code fill in the result decl. */
+ DECL_RESULT (__tu_terminate_wrapper) = NULL_TREE;
+
+ /* Make this function internal. */
+ TREE_PUBLIC (__tu_terminate_wrapper) = false;
+ DECL_EXTERNAL (__tu_terminate_wrapper) = false;
+ DECL_WEAK (__tu_terminate_wrapper) = false;
+
+ DECL_ATTRIBUTES (__tu_terminate_wrapper)
+ = tree_cons (get_identifier ("noipa"), NULL, NULL_TREE);
+ cplus_decl_attributes (&__tu_terminate_wrapper,
+ DECL_ATTRIBUTES (__tu_terminate_wrapper), 0);
+ build_terminate_wrapper();
+
+ return __tu_terminate_wrapper;
+}
+
/* Lookup a name in std::contracts/experimental, or inject it. */
static tree
@@ -1550,6 +1607,8 @@ maybe_emit_violation_handler_wrappers ()
return;
tree terminate_wrapper = terminate_fn;
+ if (flag_contracts_conservative_ipa)
+ terminate_wrapper = get_terminate_wrapper ();
/* __tu_has_violation */
start_preparsed_function (__tu_has_violation, NULL_TREE,
--
2.39.5 (Apple Git-154)