From: Nina Ranns <[email protected]>

Changes since v3

 Address Jason's review comments
 see :  
https://inbox.sourceware.org/gcc-patches/[email protected]/T/#u
 - rebased onto r16-7033-gfb6a2e3f1fa842 (includes reflection changes).

Changes since v2

 - Addressed Sandra's review comments
 - rebased onto r16-6757-g460edeb8bea11e (includes new year (c) change).

Changes since v1
 - fixed a merge error in the removal of C++2a code.
 - rebased onto r16-5785-g3b30d09ac7bbf8 (includes change to default to
   C++20).

--- 8< ---

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.
        (declare_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 | 83 +++++++++++++++++++++++++++++++++++++++++----
 gcc/doc/invoke.texi |  8 +++++
 3 files changed, 90 insertions(+), 6 deletions(-)

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 1e8a4683f040..4d1aa2384c8c 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1925,6 +1925,11 @@ fcontract-evaluation-semantic=
 C++ ObjC++ Joined RejectNegative Enum(contract_semantic) 
Var(flag_contract_evaluation_semantic) Init(3)
 -fcontract-evaluation-semantic=[ignore|observe|enforce|quick_enforce]  Select 
the contract evaluation semantic (defaults to enforce).
 
+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 205ea52774b9..a98f9a68e46d 100644
--- a/gcc/cp/contracts.cc
+++ b/gcc/cp/contracts.cc
@@ -1411,7 +1411,7 @@ static GTY(()) tree tu_has_violation = NULL_TREE;
 static GTY(()) tree tu_has_violation_exception = NULL_TREE;
 
 static void
-maybe_declare_violation_handler_wrappers ()
+declare_violation_handler_wrappers ()
 {
   if (tu_has_violation && tu_has_violation_exception)
     return;
@@ -1433,6 +1433,65 @@ maybe_declare_violation_handler_wrappers ()
                                             uint16_type_node);
 }
 
+static GTY(()) tree tu_terminate_wrapper = NULL_TREE;
+
+/* Declare a noipa wrapper around the call to std::terminate */
+
+static tree
+declare_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);
+  return tu_terminate_wrapper;
+}
+
+/* Define a noipa wrapper around the call to std::terminate */
+
+static void
+build_terminate_wrapper ()
+{
+  /* We should not be trying to build this if we never used it.  */
+  gcc_checking_assert (tu_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);
+}
+
 /* Lookup a name in std::contracts, or inject it.  */
 
 static tree
@@ -1524,9 +1583,18 @@ build_contract_handler_call (tree violation)
 void
 maybe_emit_violation_handler_wrappers ()
 {
+  /* We might need the terminate wrapper, even if we do not use the violation
+     handler wrappers.  */
+  if (tu_terminate_wrapper && flag_contracts_conservative_ipa)
+    build_terminate_wrapper ();
+
   if (!tu_has_violation && !tu_has_violation_exception)
     return;
 
+  tree terminate_wrapper = terminate_fn;
+  if (flag_contracts_conservative_ipa)
+    terminate_wrapper = tu_terminate_wrapper;
+
   /* tu_has_violation */
   start_preparsed_function (tu_has_violation, NULL_TREE,
                            SF_DEFAULT | SF_PRE_PARSED);
@@ -1547,7 +1615,7 @@ maybe_emit_violation_handler_wrappers ()
   finish_then_clause (if_observe);
   begin_else_clause (if_observe);
   /* else terminate.  */
-  finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr));
+  finish_expr_stmt (build_call_a (terminate_wrapper, 0, nullptr));
   finish_else_clause (if_observe);
   finish_if_stmt (if_observe);
   finish_return_stmt (NULL_TREE);
@@ -1597,7 +1665,7 @@ maybe_emit_violation_handler_wrappers ()
   finish_then_clause (if_observe);
   begin_else_clause (if_observe);
   /* else terminate.  */
-  finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr));
+  finish_expr_stmt (build_call_a (terminate_wrapper, 0, nullptr));
   finish_else_clause (if_observe);
   finish_if_stmt (if_observe);
   finish_return_stmt (NULL_TREE);
@@ -1970,8 +2038,11 @@ build_contract_check (tree contract)
        return NULL_TREE;
     }
 
+  tree terminate_wrapper = terminate_fn;
+  if (flag_contracts_conservative_ipa)
+    terminate_wrapper = declare_terminate_wrapper ();
   if (calls_handler)
-    maybe_declare_violation_handler_wrappers ();
+    declare_violation_handler_wrappers ();
 
   bool check_might_throw = (flag_exceptions
                            && !expr_noexcept_p (condition, tf_none));
@@ -2019,7 +2090,7 @@ build_contract_check (tree contract)
       tree handler = begin_handler ();
       finish_handler_parms (NULL_TREE, handler); /* catch (...) */
       if (quick)
-       finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr));
+       finish_expr_stmt (build_call_a (terminate_wrapper, 0, nullptr));
       else
        {
          if (viol_is_var)
@@ -2061,7 +2132,7 @@ build_contract_check (tree contract)
   tree do_check = begin_if_stmt ();
   finish_if_stmt_cond (cond, do_check);
   if (quick)
-    finish_expr_stmt (build_call_a (terminate_fn, 0, nullptr));
+    finish_expr_stmt (build_call_a (terminate_wrapper, 0, nullptr));
   else
     finish_expr_stmt (build_call_n (tu_has_violation, 2, violation, s_const));
   finish_then_clause (do_check);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index c28453598279..4c25f66322f0 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -226,6 +226,7 @@ in the following sections.
 -fconstexpr-loop-limit=@var{n}  -fconstexpr-ops-limit=@var{n}
 -fcontracts
 
-fcontract-evaluation-semantic=@r{[}ignore@r{|}observe@r{|}enforce@r{|}quick_enforce@r{]}
+-fcontracts-conservative-ipa
 -fcoroutines  -fdiagnostics-all-candidates
 -fno-elide-constructors
 -fno-enforce-eh-specs
@@ -3353,6 +3354,13 @@ fails, the execution is terminated immediately (without 
calling any handler).
 At compile-time there is no difference in behaviour between this option and
 the @code{enforce} case, since no handler is invoked at compile time.
 
+@opindex fcontracts-conservative-ipa
+@item -fcontracts-conservative-ipa
+This prevents inter-procedural analysis from taking action when the body
+of an inline function is visible in a given TU.  This allows for the case
+that contract evaluation conditions are permitted to differ between TUs which
+means that such actions would be potentially incorrect.
+
 @opindex fcoroutines
 @opindex fno-coroutines
 @item -fcoroutines
-- 
2.50.1 (Apple Git-155)

Reply via email to