diff mbox series

[COMMITTED] ada: Expose expected_throw attribute

Message ID 20230718131328.80948-1-poulhies@adacore.com
State New
Headers show
Series [COMMITTED] ada: Expose expected_throw attribute | expand

Commit Message

Marc Poulhiès July 18, 2023, 1:13 p.m. UTC
From: Alexandre Oliva <oliva@adacore.com>

Mark exception-raising subprograms with expected_throw attribute.

Document the use of the attribute in Control Flow Redundancy.

Enable marking subprograms as expected_throw with Machine_Attribute
pragmas.

gcc/ada/

	* libgnat/a-except.ads (Raise_Exception): Mark expected_throw.
	(Reraise_Occurrence): Likewise.
	(Raise_Exception_Always): Likewise.
	(Raise_From_Controlled_Operation): Likewise.
	(Reraise_Occurrence_Always): Likewise.
	(Reraise_Occurrence_No_Defer): Likewise.
	* libgnat/a-except.adb
	(Exception_Propagation.Propagate_Exception): Likewise.
	(Complete_And_Propagate_Occurrence): Likewise.
	(Raise_Exception_No_Defer): Likewise.
	(Raise_From_Signal_Handler): Likewise.
	(Raise_With_Msg): Likewise.
	(Raise_With_Location_And_Msg): Likewise.
	(Raise_Constraint_Error): Likewise.
	(Raise_Constraint_Error_Msg): Likewise.
	(Raise_Program_Error): Likewise.
	(Raise_Program_Error_Msg): Likewise.
	(Raise_Storage_Error): Likewise.
	(Raise_Storage_Error_Msg): Likewise.
	(Reraise, Rcheck_*): Likewise.
	* doc/gnat_rm/security_hardening_features.rst (Control Flow
	Hardening): Note the influence of expected_throw.
	* gnat_rm.texi: Regenerate.
	* gnat_ugn.texi: Regenerate.
	* gcc-interface/utils.cc (handle_expected_throw_attribute): New.
	(gnat_internal_attribute_table): Add expected_throw.

Tested on x86_64-pc-linux-gnu, committed on master.

---
 .../gnat_rm/security_hardening_features.rst   |   4 +-
 gcc/ada/gcc-interface/utils.cc                |  19 ++++
 gcc/ada/gnat_rm.texi                          |   6 +-
 gcc/ada/gnat_ugn.texi                         |   4 +-
 gcc/ada/libgnat/a-except.adb                  | 104 ++++++++++++++++++
 gcc/ada/libgnat/a-except.ads                  |  14 +++
 6 files changed, 146 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/gcc/ada/doc/gnat_rm/security_hardening_features.rst b/gcc/ada/doc/gnat_rm/security_hardening_features.rst
index e057af2ea12..015b9ce3533 100644
--- a/gcc/ada/doc/gnat_rm/security_hardening_features.rst
+++ b/gcc/ada/doc/gnat_rm/security_hardening_features.rst
@@ -500,7 +500,9 @@  gets modified as follows:
 
 Verification may also be performed before No_Return calls, whether all
 of them, with :switch:`-fhardcfr-check-noreturn-calls=always`; all but
-internal subprograms involved in exception-raising or -reraising, with
+internal subprograms involved in exception-raising or -reraising or
+subprograms explicitly marked with both :samp:`No_Return` and
+:samp:`Machine_Attribute` :samp:`expected_throw` pragmas, with
 :switch:`-fhardcfr-check-noreturn-calls=no-xthrow` (default); only
 nothrow ones, with :switch:`-fhardcfr-check-noreturn-calls=nothrow`;
 or none, with :switch:`-fhardcfr-check-noreturn-calls=never`.
diff --git a/gcc/ada/gcc-interface/utils.cc b/gcc/ada/gcc-interface/utils.cc
index 8f1861b848e..d0a13d2af33 100644
--- a/gcc/ada/gcc-interface/utils.cc
+++ b/gcc/ada/gcc-interface/utils.cc
@@ -87,6 +87,7 @@  tree gnat_raise_decls_ext[(int) LAST_REASON_CODE + 1];
 /* Forward declarations for handlers of attributes.  */
 static tree handle_const_attribute (tree *, tree, tree, int, bool *);
 static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *);
+static tree handle_expected_throw_attribute (tree *, tree, tree, int, bool *);
 static tree handle_pure_attribute (tree *, tree, tree, int, bool *);
 static tree handle_novops_attribute (tree *, tree, tree, int, bool *);
 static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *);
@@ -143,6 +144,8 @@  const struct attribute_spec gnat_internal_attribute_table[] =
     handle_const_attribute, NULL },
   { "nothrow",      0, 0,  true,  false, false, false,
     handle_nothrow_attribute, NULL },
+  { "expected_throw", 0, 0, true,  false, false, false,
+    handle_expected_throw_attribute, NULL },
   { "pure",         0, 0,  true,  false, false, false,
     handle_pure_attribute, NULL },
   { "no vops",      0, 0,  true,  false, false, false,
@@ -6379,6 +6382,22 @@  handle_nothrow_attribute (tree *node, tree ARG_UNUSED (name),
   return NULL_TREE;
 }
 
+/* Handle a "expected_throw" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_expected_throw_attribute (tree *node, tree ARG_UNUSED (name),
+				 tree ARG_UNUSED (args), int ARG_UNUSED (flags),
+				 bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == FUNCTION_DECL)
+    /* No flag to set here.  */;
+  else
+    *no_add_attrs = true;
+
+  return NULL_TREE;
+}
+
 /* Handle a "pure" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/ada/gnat_rm.texi b/gcc/ada/gnat_rm.texi
index 066c066d19d..b7e098331e9 100644
--- a/gcc/ada/gnat_rm.texi
+++ b/gcc/ada/gnat_rm.texi
@@ -19,7 +19,7 @@ 
 
 @copying
 @quotation
-GNAT Reference Manual , Jul 10, 2023
+GNAT Reference Manual , Jul 17, 2023
 
 AdaCore
 
@@ -29666,7 +29666,9 @@  end;
 
 Verification may also be performed before No_Return calls, whether all
 of them, with @code{-fhardcfr-check-noreturn-calls=always}; all but
-internal subprograms involved in exception-raising or -reraising, with
+internal subprograms involved in exception-raising or -reraising or
+subprograms explicitly marked with both @code{No_Return} and
+@code{Machine_Attribute} @code{expected_throw} pragmas, with
 @code{-fhardcfr-check-noreturn-calls=no-xthrow} (default); only
 nothrow ones, with @code{-fhardcfr-check-noreturn-calls=nothrow};
 or none, with @code{-fhardcfr-check-noreturn-calls=never}.
diff --git a/gcc/ada/gnat_ugn.texi b/gcc/ada/gnat_ugn.texi
index 2721251c4e4..78f9b87a82e 100644
--- a/gcc/ada/gnat_ugn.texi
+++ b/gcc/ada/gnat_ugn.texi
@@ -19,7 +19,7 @@ 
 
 @copying
 @quotation
-GNAT User's Guide for Native Platforms , Jul 10, 2023
+GNAT User's Guide for Native Platforms , Jul 17, 2023
 
 AdaCore
 
@@ -29531,8 +29531,8 @@  to permit their use in free software.
 
 @printindex ge
 
-@anchor{d1}@w{                              }
 @anchor{gnat_ugn/gnat_utility_programs switches-related-to-project-files}@w{                              }
+@anchor{d1}@w{                              }
 
 @c %**end of body
 @bye
diff --git a/gcc/ada/libgnat/a-except.adb b/gcc/ada/libgnat/a-except.adb
index 20a773661ae..dd5edaf1a9c 100644
--- a/gcc/ada/libgnat/a-except.adb
+++ b/gcc/ada/libgnat/a-except.adb
@@ -229,6 +229,7 @@  package body Ada.Exceptions is
 
       procedure Propagate_Exception (Excep : Exception_Occurrence);
       pragma No_Return (Propagate_Exception);
+      pragma Machine_Attribute (Propagate_Exception, "expected_throw");
       --  This procedure propagates the exception represented by Excep
 
    end Exception_Propagation;
@@ -256,6 +257,8 @@  package body Ada.Exceptions is
 
    procedure Complete_And_Propagate_Occurrence (X : EOA);
    pragma No_Return (Complete_And_Propagate_Occurrence);
+   pragma Machine_Attribute (Complete_And_Propagate_Occurrence,
+                             "expected_throw");
    --  This is a simple wrapper to Complete_Occurrence and
    --  Exception_Propagation.Propagate_Exception.
 
@@ -280,6 +283,7 @@  package body Ada.Exceptions is
     (Ada, Raise_Exception_No_Defer,
      "ada__exceptions__raise_exception_no_defer");
    pragma No_Return (Raise_Exception_No_Defer);
+   pragma Machine_Attribute (Raise_Exception_No_Defer, "expected_throw");
    --  Similar to Raise_Exception, but with no abort deferral
 
    procedure Raise_From_Signal_Handler
@@ -288,6 +292,7 @@  package body Ada.Exceptions is
    pragma Export
      (C, Raise_From_Signal_Handler, "__gnat_raise_from_signal_handler");
    pragma No_Return (Raise_From_Signal_Handler);
+   pragma Machine_Attribute (Raise_From_Signal_Handler, "expected_throw");
    --  This routine is used to raise an exception from a signal handler. The
    --  signal handler has already stored the machine state (i.e. the state that
    --  corresponds to the location at which the signal was raised). E is the
@@ -301,6 +306,7 @@  package body Ada.Exceptions is
 
    procedure Raise_With_Msg (E : Exception_Id);
    pragma No_Return (Raise_With_Msg);
+   pragma Machine_Attribute (Raise_With_Msg, "expected_throw");
    pragma Export (C, Raise_With_Msg, "__gnat_raise_with_msg");
    --  Raises an exception with given exception id value. A message
    --  is associated with the raise, and has already been stored in the
@@ -314,6 +320,7 @@  package body Ada.Exceptions is
       C : Integer := 0;
       M : System.Address := System.Null_Address);
    pragma No_Return (Raise_With_Location_And_Msg);
+   pragma Machine_Attribute (Raise_With_Location_And_Msg, "expected_throw");
    --  Raise an exception with given exception id value. A filename and line
    --  number is associated with the raise and is stored in the exception
    --  occurrence and in addition a column and a string message M may be
@@ -321,6 +328,7 @@  package body Ada.Exceptions is
 
    procedure Raise_Constraint_Error (File : System.Address; Line : Integer);
    pragma No_Return (Raise_Constraint_Error);
+   pragma Machine_Attribute (Raise_Constraint_Error, "expected_throw");
    pragma Export (C, Raise_Constraint_Error, "__gnat_raise_constraint_error");
    --  Raise constraint error with file:line information
 
@@ -330,12 +338,14 @@  package body Ada.Exceptions is
       Column : Integer;
       Msg    : System.Address);
    pragma No_Return (Raise_Constraint_Error_Msg);
+   pragma Machine_Attribute (Raise_Constraint_Error_Msg, "expected_throw");
    pragma Export
      (C, Raise_Constraint_Error_Msg, "__gnat_raise_constraint_error_msg");
    --  Raise constraint error with file:line:col + msg information
 
    procedure Raise_Program_Error (File : System.Address; Line : Integer);
    pragma No_Return (Raise_Program_Error);
+   pragma Machine_Attribute (Raise_Program_Error, "expected_throw");
    pragma Export (C, Raise_Program_Error, "__gnat_raise_program_error");
    --  Raise program error with file:line information
 
@@ -344,12 +354,14 @@  package body Ada.Exceptions is
       Line : Integer;
       Msg  : System.Address);
    pragma No_Return (Raise_Program_Error_Msg);
+   pragma Machine_Attribute (Raise_Program_Error_Msg, "expected_throw");
    pragma Export
      (C, Raise_Program_Error_Msg, "__gnat_raise_program_error_msg");
    --  Raise program error with file:line + msg information
 
    procedure Raise_Storage_Error (File : System.Address; Line : Integer);
    pragma No_Return (Raise_Storage_Error);
+   pragma Machine_Attribute (Raise_Storage_Error, "expected_throw");
    pragma Export (C, Raise_Storage_Error, "__gnat_raise_storage_error");
    --  Raise storage error with file:line information
 
@@ -358,6 +370,7 @@  package body Ada.Exceptions is
       Line : Integer;
       Msg  : System.Address);
    pragma No_Return (Raise_Storage_Error_Msg);
+   pragma Machine_Attribute (Raise_Storage_Error_Msg, "expected_throw");
    pragma Export
      (C, Raise_Storage_Error_Msg, "__gnat_raise_storage_error_msg");
    --  Raise storage error with file:line + reason msg information
@@ -385,6 +398,7 @@  package body Ada.Exceptions is
 
    procedure Reraise;
    pragma No_Return (Reraise);
+   pragma Machine_Attribute (Reraise, "expected_throw");
    pragma Export (C, Reraise, "__gnat_reraise");
    --  Reraises the exception referenced by the Current_Excep field
    --  of the TSD (all fields of this exception occurrence are set).
@@ -632,6 +646,96 @@  package body Ada.Exceptions is
    pragma No_Return (Rcheck_CE_Invalid_Data_Ext);
    pragma No_Return (Rcheck_CE_Range_Check_Ext);
 
+   --  These procedures are all expected to raise an exception.
+   --  These attributes are not visible to callers; they are made
+   --  visible in trans.c:build_raise_check.
+
+   pragma Machine_Attribute (Rcheck_CE_Access_Check,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Null_Access_Parameter,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Discriminant_Check,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Divide_By_Zero,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Explicit_Raise,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Index_Check,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Invalid_Data,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Length_Check,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Null_Exception_Id,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Null_Not_Allowed,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Overflow_Check,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Partition_Check,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Range_Check,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Tag_Check,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Access_Before_Elaboration,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Accessibility_Check,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Address_Of_Intrinsic,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Aliased_Parameters,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_All_Guards_Closed,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Bad_Predicated_Generic_Type,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Build_In_Place_Mismatch,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Current_Task_In_Entry_Body,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Duplicated_Entry_Address,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Explicit_Raise,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Implicit_Return,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Misaligned_Address_Value,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Missing_Return,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Non_Transportable_Actual,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Overlaid_Controlled_Object,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Potentially_Blocking_Operation,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Stream_Operation_Not_Allowed,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Stubbed_Subprogram_Called,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Unchecked_Union_Restriction,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_PE_Finalize_Raised_Exception,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_SE_Empty_Storage_Pool,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_SE_Explicit_Raise,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_SE_Infinite_Recursion,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_SE_Object_Too_Large,
+                             "expected_throw");
+
+   pragma Machine_Attribute (Rcheck_CE_Access_Check_Ext,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Index_Check_Ext,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Invalid_Data_Ext,
+                             "expected_throw");
+   pragma Machine_Attribute (Rcheck_CE_Range_Check_Ext,
+                             "expected_throw");
+
    --  Make all of these procedures callable from strub contexts.
    --  These attributes are not visible to callers; they are made
    --  visible in trans.c:build_raise_check.
diff --git a/gcc/ada/libgnat/a-except.ads b/gcc/ada/libgnat/a-except.ads
index 7949b5907b6..5583bf5504d 100644
--- a/gcc/ada/libgnat/a-except.ads
+++ b/gcc/ada/libgnat/a-except.ads
@@ -163,6 +163,14 @@  private
 
    Null_Id : constant Exception_Id := null;
 
+   pragma Machine_Attribute (Raise_Exception, "expected_throw");
+   pragma Machine_Attribute (Reraise_Occurrence, "expected_throw");
+   --  Tell the compiler that an exception is likely after calling
+   --  these subprograms. This could eventually be used for hot/cold
+   --  partitioning. For now, this only enables the control flow
+   --  redundancy to avoid duplicating a check before the No_Return
+   --  call and in the exception handler for the call.
+
    -------------------------
    -- Private Subprograms --
    -------------------------
@@ -177,6 +185,7 @@  private
 
    procedure Raise_Exception_Always (E : Exception_Id; Message : String := "");
    pragma No_Return (Raise_Exception_Always);
+   pragma Machine_Attribute (Raise_Exception_Always, "expected_throw");
    pragma Export (Ada, Raise_Exception_Always, "__gnat_raise_exception");
    --  This differs from Raise_Exception only in that the caller has determined
    --  that for sure the parameter E is not null, and that therefore no check
@@ -195,6 +204,9 @@  private
            "__gnat_raise_from_controlled_operation");
    --  Raise Program_Error, providing information about X (an exception raised
    --  during a controlled operation) in the exception message.
+   pragma Machine_Attribute (Raise_From_Controlled_Operation,
+                             "expected_throw");
+   --  Mark it like internal exception-raising subprograms
 
    procedure Reraise_Library_Exception_If_Any;
    pragma Export
@@ -205,6 +217,7 @@  private
 
    procedure Reraise_Occurrence_Always (X : Exception_Occurrence);
    pragma No_Return (Reraise_Occurrence_Always);
+   pragma Machine_Attribute (Reraise_Occurrence_Always, "expected_throw");
    --  This differs from Raise_Occurrence only in that the caller guarantees
    --  that for sure the parameter X is not the null occurrence, and that
    --  therefore this procedure cannot return. The expander uses this routine
@@ -212,6 +225,7 @@  private
 
    procedure Reraise_Occurrence_No_Defer (X : Exception_Occurrence);
    pragma No_Return (Reraise_Occurrence_No_Defer);
+   pragma Machine_Attribute (Reraise_Occurrence_No_Defer, "expected_throw");
    --  Exactly like Reraise_Occurrence, except that abort is not deferred
    --  before the call and the parameter X is known not to be the null
    --  occurrence. This is used in generated code when it is known that abort