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)
