mirror of
https://github.com/gcc-mirror/gcc.git
synced 2026-05-06 14:59:39 +02:00
c++, contracts: Work around GCC IPA bug, PR121936 by wrapping terminate.
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. gcc/ChangeLog: * doc/invoke.texi: Document -fcontracts-conservative-ipa. Co-Authored-by: Iain Sandoe <iain@sandoe.co.uk> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
This commit is contained in:
@@ -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).
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -3355,6 +3356,14 @@ 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
|
||||
@opindex fno-contracts-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
|
||||
|
||||
Reference in New Issue
Block a user