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)

Reply via email to