public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/users/aoliva/heads/strub)] make builtins callable
@ 2021-07-26 15:43 Alexandre Oliva
  0 siblings, 0 replies; 7+ messages in thread
From: Alexandre Oliva @ 2021-07-26 15:43 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:32ad884ca9b25daf7610e32e77e2a88a4037404f

commit 32ad884ca9b25daf7610e32e77e2a88a4037404f
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Mon Jul 26 12:15:36 2021 -0300

    make builtins callable

Diff:
---
 gcc/ipa-strub.c | 251 +++++++++++++++++++++-----------------------------------
 1 file changed, 95 insertions(+), 156 deletions(-)

diff --git a/gcc/ipa-strub.c b/gcc/ipa-strub.c
index 7b05321e980..18c1c9fedd4 100644
--- a/gcc/ipa-strub.c
+++ b/gcc/ipa-strub.c
@@ -438,6 +438,34 @@ strub_from_body_p (cgraph_node *node)
   return false;
 }
 
+/* Return true iff node is associated with a builtin that should be callable
+   from strub contexts.  */
+static inline bool
+strub_callable_builtin_p (cgraph_node *node)
+{
+  if (DECL_BUILT_IN_CLASS (node->decl) != BUILT_IN_NORMAL)
+    return false;
+
+  enum built_in_function fcode = DECL_FUNCTION_CODE (node->decl);
+
+  switch (fcode)
+    {
+    case BUILT_IN_NONE:
+      gcc_unreachable ();
+
+      /* ??? Make all builtins callable.  We wish to make any builtin call the
+	 compiler might introduce on its own callable.  Anything that is
+	 predictable enough as to be known not to allow stack data that should
+	 be strubbed to unintentionally escape to non-strub contexts can be
+	 allowed, and pretty much every builtin appears to fit this description.
+	 The exceptions to this rule seem to be rare, and only available as
+	 explicit __builtin calls, so let's keep it simple and allow all of
+	 them...  */
+    default:
+      return true;
+    }
+}
+
 static enum strub_mode
 compute_strub_mode (cgraph_node *node, tree strub_attr)
 {
@@ -495,7 +523,8 @@ compute_strub_mode (cgraph_node *node, tree strub_attr)
     = (!strub_flag_disabled
        && (strub_attr
 	   ? req_mode == STRUB_CALLABLE
-	   : strub_flag_viable));
+	   : (strub_flag_viable
+	      || strub_callable_builtin_p (node))));
 
   /* This is a shorthand for either strub-enabled mode.  */
   const bool consider_strub
@@ -845,6 +874,49 @@ strub_inlinable_p (cgraph_node *callee, cgraph_node *caller)
   return false;
 }
 
+/* Check that strub functions don't call non-strub functions, and that
+   always_inline strub functions are only called by strub
+   functions.  */
+static void
+verify_strub ()
+{
+  cgraph_node *node;
+
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+  {
+    enum strub_mode caller_mode = get_strub_mode (node);
+    bool strub_context
+      = (caller_mode == STRUB_AT_CALLS
+	 || caller_mode == STRUB_WRAPPED
+	 || caller_mode == STRUB_INLINABLE);
+
+    for (cgraph_edge *e = node->callees; e; e = e->next_callee)
+      if (e->indirect_unknown_callee)
+	{
+	  if (!strub_context)
+	    continue;
+
+	  tree callee_fntype = gimple_call_fntype (e->call_stmt);
+	  enum strub_mode callee_mode
+	    = get_strub_mode_from_type (callee_fntype);
+
+	  if (callee_mode == STRUB_DISABLED
+	      || callee_mode == STRUB_INTERNAL)
+	    error_at (gimple_location (e->call_stmt),
+		      "indirect non-strub call in strub context %qD",
+		      node->decl);
+	}
+      else if (!strub_callable_from_p (e->callee, node))
+	error_at (gimple_location (e->call_stmt),
+		  "calling non-strub %qD in strub context %qD",
+		  e->callee->decl, node->decl);
+  }
+
+  /* ??? Check strub-wise pointer type compatibility of variables and
+     functions, or is this already taken care of on account of the
+     attribute's being marked as affecting type identity?  */
+}
+
 namespace {
 
 const pass_data pass_data_ipa_strub_mode = {
@@ -1014,116 +1086,6 @@ public:
 
 } // anon namespace
 
-#if 0
-static bool
-may_throw_p (gcall *stmt)
-{
-  return flag_exceptions && !gimple_call_nothrow_p (stmt);
-}
-
-static bool
-strub_this_call_p (gcall *stmt)
-{
-  if (gimple_call_internal_p (stmt))
-    return false;
-
-  /* If there's no outgoing path in which to do the scrubbing, don't
-     bother.  */
-  if (gimple_call_noreturn_p (stmt) && !may_throw_p (stmt))
-    return false;
-
-  /* ??? Maybe non-mandatory tail calls should be disabled for
-     scrubbing.  Or maybe it won't matter, as long as both tail-caller
-     and callee are scrubbing-capable.  */
-  if (gimple_call_must_tail_p (stmt) || gimple_call_tail_p (stmt))
-    return false;
-
-  if (gimple_alloca_call_p (stmt))
-    return true;
-
-  tree fndecl = gimple_call_fndecl (stmt);
-  if (!fndecl)
-    return true;
-
-  if (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
-    return true;
-
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-
-  switch (fcode)
-    {
-    CASE_BUILT_IN_ALLOCA:
-      return true;
-
-    case BUILT_IN_NONE:
-      return true;
-
-    case BUILT_IN___STRUB_ENTER:
-    case BUILT_IN___STRUB_UPDATE:
-    case BUILT_IN___STRUB_LEAVE:
-      return false;
-
-    case BUILT_IN_CLASSIFY_TYPE:
-    case BUILT_IN_CONSTANT_P:
-      return false;
-
-    case BUILT_IN_RETURN_ADDRESS:
-    case BUILT_IN_FRAME_ADDRESS:
-    case BUILT_IN_STACK_ADDRESS:
-    case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
-      return false;
-
-    case BUILT_IN_STACK_SAVE:
-    case BUILT_IN_STACK_RESTORE:
-    case BUILT_IN_ASAN_ALLOCAS_UNPOISON:
-      return false;
-
-    case BUILT_IN_SETJMP_SETUP:
-    case BUILT_IN_SETJMP_RECEIVER:
-    case BUILT_IN_LONGJMP:
-    case BUILT_IN_NONLOCAL_GOTO:
-    case BUILT_IN_UPDATE_SETJMP_BUF:
-    case BUILT_IN_TRAP:
-    case BUILT_IN_UNREACHABLE:
-      return false;
-
-    case BUILT_IN_UNWIND_INIT:
-    case BUILT_IN_DWARF_CFA:
-#ifdef DWARF2_UNWIND_INFO
-    case BUILT_IN_DWARF_SP_COLUMN:
-    case BUILT_IN_INIT_DWARF_REG_SIZES:
-#endif
-    case BUILT_IN_FROB_RETURN_ADDR:
-    case BUILT_IN_EXTRACT_RETURN_ADDR:
-    case BUILT_IN_EH_RETURN:
-    case BUILT_IN_EH_RETURN_DATA_REGNO:
-    case BUILT_IN_EXTEND_POINTER:
-    case BUILT_IN_EH_POINTER:
-    case BUILT_IN_EH_FILTER:
-    case BUILT_IN_EH_COPY_VALUES:
-      return false;
-
-    case BUILT_IN_VA_START:
-    case BUILT_IN_VA_END:
-    case BUILT_IN_VA_COPY:
-    case BUILT_IN_EXPECT:
-    case BUILT_IN_EXPECT_WITH_PROBABILITY:
-    case BUILT_IN_ASSUME_ALIGNED:
-    case BUILT_IN_PREFETCH:
-      return false;
-
-    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
-    case BUILT_IN_OBJECT_SIZE:
-    case BUILT_IN_THREAD_POINTER:
-    case BUILT_IN_SET_THREAD_POINTER:
-      return false;
-
-    default:
-      return true;
-    }
-}
-#endif
-
 typedef hash_set<tree> indirect_parms_t;
 
 static tree
@@ -1414,6 +1376,9 @@ pass_ipa_strub_mode::execute (function *)
 
   bool any_strub = false;
 
+  /* Go through the functions twice, once over non-aliases, and then over
+     aliases, so that aliases can reuse the mode computation of their ultimate
+     targets.  */
   for (int aliases = 0; aliases <= 1; aliases++)
     FOR_EACH_FUNCTION (onode)
     {
@@ -1428,6 +1393,9 @@ pass_ipa_strub_mode::execute (function *)
 
   if (!any_strub)
     flag_strub = 0;
+  else
+    /* Verify before any inlining or other transformations.  */
+    verify_strub ();
 
   return 0;
 }
@@ -1438,49 +1406,6 @@ make_pass_ipa_strub_mode (gcc::context *ctxt)
   return new pass_ipa_strub_mode (ctxt);
 }
 
-/* Check that strub functions don't call non-strub functions, and that
-   always_inline strub functions are only called by strub
-   functions.  */
-static void
-verify_strub ()
-{
-  cgraph_node *node;
-
-  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
-  {
-    enum strub_mode caller_mode = get_strub_mode (node);
-    bool strub_context
-      = (caller_mode == STRUB_AT_CALLS
-	 || caller_mode == STRUB_WRAPPED
-	 || caller_mode == STRUB_INLINABLE);
-
-    for (cgraph_edge *e = node->callees; e; e = e->next_callee)
-      if (e->indirect_unknown_callee)
-	{
-	  if (!strub_context)
-	    continue;
-
-	  tree callee_fntype = gimple_call_fntype (e->call_stmt);
-	  enum strub_mode callee_mode
-	    = get_strub_mode_from_type (callee_fntype);
-
-	  if (callee_mode == STRUB_DISABLED
-	      || callee_mode == STRUB_INTERNAL)
-	    error_at (gimple_location (e->call_stmt),
-		      "indirect non-strub call in strub context %qD",
-		      node->decl);
-	}
-      else if (!strub_callable_from_p (e->callee, node))
-	error_at (gimple_location (e->call_stmt),
-		  "calling non-strub %qD in strub context %qD",
-		  e->callee->decl, node->decl);
-  }
-
-  /* ??? Check strub-wise pointer type compatibility of variables and
-     functions, or is this already taken care of on account of the
-     attribute's being marked as affecting type identity?  */
-}
-
 unsigned int
 pass_ipa_strub::execute (function *)
 {
@@ -2291,7 +2216,17 @@ pass_ipa_strub::execute (function *)
 		  }
 		gimple_call_set_arg (call, 1, arg);
 		update_stmt (call);
-		e->redirect_callee (cgraph_node::get_create (bvacopy));
+
+		/* If we are causing the cgraph_node for va_copy to be needed,
+		   compute a strub mode for it to make it callable, otherwise
+		   verify_strub would reject it.  */
+		cgraph_node *vacopy_node = cgraph_node::get (bvacopy);
+		if (!vacopy_node)
+		  {
+		    vacopy_node = cgraph_node::get_create (bvacopy);
+		    set_strub_mode (vacopy_node);
+		  }
+		e->redirect_callee (vacopy_node);
 	      }
 	    else if (fndecl && apply_args
 		     && fndecl_built_in_p (fndecl, BUILT_IN_APPLY_ARGS))
@@ -2418,7 +2353,11 @@ pass_ipa_strub::execute (function *)
 #endif
   }
 
-  verify_strub ();
+  if (flag_checking)
+    /* We've already verified before any inlining or other transformations.
+       Recheck after strub transformations only if checking is enabled, since
+       they should not introduce any incompatibilities.  */
+    verify_strub ();
 
   return 0;
 }


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [gcc(refs/users/aoliva/heads/strub)] make builtins callable
@ 2021-07-28  4:30 Alexandre Oliva
  0 siblings, 0 replies; 7+ messages in thread
From: Alexandre Oliva @ 2021-07-28  4:30 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:8efece23e8d3c6c98983bf6a75d750947183d5fe

commit 8efece23e8d3c6c98983bf6a75d750947183d5fe
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Mon Jul 26 12:15:36 2021 -0300

    make builtins callable

Diff:
---
 gcc/builtins.c  |   8 -
 gcc/ipa-strub.c | 588 ++++++++++++++++++++++++++++++++++++++------------------
 2 files changed, 399 insertions(+), 197 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 373b4e90f70..23fe484dc7e 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -7987,14 +7987,6 @@ expand_builtin_strub_leave (tree exp)
   tree wmtree = fold_build2 (MEM_REF, wmtype, wmptr,
 			     build_int_cst (TREE_TYPE (wmptr), 0));
   rtx wmark = expand_expr (wmtree, NULL_RTX, ptr_mode, EXPAND_MEMORY);
-
-  /* If the function called between enter and leave is const or pure,
-     we may assume it won't change the watermark passed indirectly to
-     it.  These ensure the value is neither dropped nor assumed
-     unchanged.  */
-  emit_insn (gen_rtx_USE (VOIDmode, wmark));
-  emit_insn (gen_rtx_CLOBBER (VOIDmode, wmark));
-
   rtx wmarkr = force_reg (ptr_mode, wmark);
 
 #ifndef STACK_GROWS_DOWNWARD
diff --git a/gcc/ipa-strub.c b/gcc/ipa-strub.c
index 7b05321e980..a32b9b9ead1 100644
--- a/gcc/ipa-strub.c
+++ b/gcc/ipa-strub.c
@@ -45,7 +45,6 @@ along with GCC; see the file COPYING3.  If not see
 #include "symbol-summary.h"
 #include "ipa-prop.h"
 #include "ipa-fnsummary.h"
-#include "symtab-thunks.h"
 #include "gimple-fold.h"
 #include "fold-const.h"
 #include "gimple-walk.h"
@@ -59,7 +58,26 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic.h"
 #include "intl.h"
 #include "ipa-strub.h"
-#include "attr-fnspec.h"
+
+#if BUILDING_GCC_MAJOR >= 11
+# include "symtab-thunks.h"
+# include "attr-fnspec.h"
+# define HAVE_ATTR_FNSPEC 1
+# define FOR_GCC_11P 1
+#else
+# define HAVE_ATTR_FNSPEC 0
+# define FOR_GCC_11P 0
+#endif
+
+/* Const and pure functions that gain a watermark parameter for strub purposes
+   are still regarded as such, which may cause the inline expansions of the
+   __strub builtins to malfunction.  Ideally, attribute "fn spec" would enable
+   us to inform the backend about requirements and side effects of the call, but
+   call_fusage building in calls.c:expand_call does not even look at
+   attr_fnspec, so we resort to asm loads and updates to attain an equivalent
+   effect.  Once expand_call gains the ability to issue extra memory uses and
+   clobbers based on pure/const function's fnspec, we can define this to 1.  */
+#define ATTR_FNSPEC_DECONST_WATERMARK 0
 
 enum strub_mode {
   /* This mode denotes a regular function, that does not require stack
@@ -438,6 +456,34 @@ strub_from_body_p (cgraph_node *node)
   return false;
 }
 
+/* Return true iff node is associated with a builtin that should be callable
+   from strub contexts.  */
+static inline bool
+strub_callable_builtin_p (cgraph_node *node)
+{
+  if (DECL_BUILT_IN_CLASS (node->decl) != BUILT_IN_NORMAL)
+    return false;
+
+  enum built_in_function fcode = DECL_FUNCTION_CODE (node->decl);
+
+  switch (fcode)
+    {
+    case BUILT_IN_NONE:
+      gcc_unreachable ();
+
+      /* ??? Make all builtins callable.  We wish to make any builtin call the
+	 compiler might introduce on its own callable.  Anything that is
+	 predictable enough as to be known not to allow stack data that should
+	 be strubbed to unintentionally escape to non-strub contexts can be
+	 allowed, and pretty much every builtin appears to fit this description.
+	 The exceptions to this rule seem to be rare, and only available as
+	 explicit __builtin calls, so let's keep it simple and allow all of
+	 them...  */
+    default:
+      return true;
+    }
+}
+
 static enum strub_mode
 compute_strub_mode (cgraph_node *node, tree strub_attr)
 {
@@ -495,7 +541,8 @@ compute_strub_mode (cgraph_node *node, tree strub_attr)
     = (!strub_flag_disabled
        && (strub_attr
 	   ? req_mode == STRUB_CALLABLE
-	   : strub_flag_viable));
+	   : (strub_flag_viable
+	      || strub_callable_builtin_p (node))));
 
   /* This is a shorthand for either strub-enabled mode.  */
   const bool consider_strub
@@ -730,6 +777,26 @@ set_strub_mode (cgraph_node *node)
 {
   tree attr = get_strub_attr_from_decl (node->decl);
 
+  if (attr)
+    switch (enum strub_mode mode = get_strub_mode_from_attr (attr))
+      {
+	/* These can't have been requested through user attributes, so we must
+	   have already gone through them.  */
+      case STRUB_WRAPPER:
+      case STRUB_WRAPPED:
+      case STRUB_INLINABLE:
+	return mode;
+
+      case STRUB_DISABLED:
+      case STRUB_AT_CALLS:
+      case STRUB_INTERNAL:
+      case STRUB_CALLABLE:
+	break;
+
+      default:
+	gcc_unreachable ();
+      }
+
   cgraph_node *xnode = node;
   if (node->alias)
     xnode = node->ultimate_alias_target ();
@@ -845,6 +912,49 @@ strub_inlinable_p (cgraph_node *callee, cgraph_node *caller)
   return false;
 }
 
+/* Check that strub functions don't call non-strub functions, and that
+   always_inline strub functions are only called by strub
+   functions.  */
+static void
+verify_strub ()
+{
+  cgraph_node *node;
+
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+  {
+    enum strub_mode caller_mode = get_strub_mode (node);
+    bool strub_context
+      = (caller_mode == STRUB_AT_CALLS
+	 || caller_mode == STRUB_WRAPPED
+	 || caller_mode == STRUB_INLINABLE);
+
+    for (cgraph_edge *e = node->callees; e; e = e->next_callee)
+      if (e->indirect_unknown_callee)
+	{
+	  if (!strub_context)
+	    continue;
+
+	  tree callee_fntype = gimple_call_fntype (e->call_stmt);
+	  enum strub_mode callee_mode
+	    = get_strub_mode_from_type (callee_fntype);
+
+	  if (callee_mode == STRUB_DISABLED
+	      || callee_mode == STRUB_INTERNAL)
+	    error_at (gimple_location (e->call_stmt),
+		      "indirect non-strub call in strub context %qD",
+		      node->decl);
+	}
+      else if (!strub_callable_from_p (e->callee, node))
+	error_at (gimple_location (e->call_stmt),
+		  "calling non-strub %qD in strub context %qD",
+		  e->callee->decl, node->decl);
+  }
+
+  /* ??? Check strub-wise pointer type compatibility of variables and
+     functions, or is this already taken care of on account of the
+     attribute's being marked as affecting type identity?  */
+}
+
 namespace {
 
 const pass_data pass_data_ipa_strub_mode = {
@@ -907,8 +1017,6 @@ public:
 	   NULL, NULL);						\
 	TREE_NOTHROW (decl) = true;				\
 	set_builtin_decl ((CODE), decl, true);			\
-	set_strub_mode_to (cgraph_node::get_create (decl),	\
-			   STRUB_CALLABLE);			\
       }								\
     return decl;						\
   }
@@ -926,7 +1034,7 @@ public:
       {								\
 	tree type = build_function_type_list FNTYPELIST;	\
 	tree attrs = NULL;					\
-	if (FNSPEC)						\
+	if (FNSPEC && HAVE_ATTR_FNSPEC)				\
 	  attrs = tree_cons (get_identifier ("fn spec"),	\
 			     build_tree_list			\
 			     (NULL_TREE,			\
@@ -939,8 +1047,6 @@ public:
 	   "__strub_" #NAME, attrs);				\
 	TREE_NOTHROW (decl) = true;				\
 	set_builtin_decl ((CODE), decl, true);			\
-	set_strub_mode_to (cgraph_node::get_create (decl),	\
-			   STRUB_CALLABLE);			\
       }								\
     return decl;						\
   }
@@ -1014,116 +1120,6 @@ public:
 
 } // anon namespace
 
-#if 0
-static bool
-may_throw_p (gcall *stmt)
-{
-  return flag_exceptions && !gimple_call_nothrow_p (stmt);
-}
-
-static bool
-strub_this_call_p (gcall *stmt)
-{
-  if (gimple_call_internal_p (stmt))
-    return false;
-
-  /* If there's no outgoing path in which to do the scrubbing, don't
-     bother.  */
-  if (gimple_call_noreturn_p (stmt) && !may_throw_p (stmt))
-    return false;
-
-  /* ??? Maybe non-mandatory tail calls should be disabled for
-     scrubbing.  Or maybe it won't matter, as long as both tail-caller
-     and callee are scrubbing-capable.  */
-  if (gimple_call_must_tail_p (stmt) || gimple_call_tail_p (stmt))
-    return false;
-
-  if (gimple_alloca_call_p (stmt))
-    return true;
-
-  tree fndecl = gimple_call_fndecl (stmt);
-  if (!fndecl)
-    return true;
-
-  if (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
-    return true;
-
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-
-  switch (fcode)
-    {
-    CASE_BUILT_IN_ALLOCA:
-      return true;
-
-    case BUILT_IN_NONE:
-      return true;
-
-    case BUILT_IN___STRUB_ENTER:
-    case BUILT_IN___STRUB_UPDATE:
-    case BUILT_IN___STRUB_LEAVE:
-      return false;
-
-    case BUILT_IN_CLASSIFY_TYPE:
-    case BUILT_IN_CONSTANT_P:
-      return false;
-
-    case BUILT_IN_RETURN_ADDRESS:
-    case BUILT_IN_FRAME_ADDRESS:
-    case BUILT_IN_STACK_ADDRESS:
-    case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
-      return false;
-
-    case BUILT_IN_STACK_SAVE:
-    case BUILT_IN_STACK_RESTORE:
-    case BUILT_IN_ASAN_ALLOCAS_UNPOISON:
-      return false;
-
-    case BUILT_IN_SETJMP_SETUP:
-    case BUILT_IN_SETJMP_RECEIVER:
-    case BUILT_IN_LONGJMP:
-    case BUILT_IN_NONLOCAL_GOTO:
-    case BUILT_IN_UPDATE_SETJMP_BUF:
-    case BUILT_IN_TRAP:
-    case BUILT_IN_UNREACHABLE:
-      return false;
-
-    case BUILT_IN_UNWIND_INIT:
-    case BUILT_IN_DWARF_CFA:
-#ifdef DWARF2_UNWIND_INFO
-    case BUILT_IN_DWARF_SP_COLUMN:
-    case BUILT_IN_INIT_DWARF_REG_SIZES:
-#endif
-    case BUILT_IN_FROB_RETURN_ADDR:
-    case BUILT_IN_EXTRACT_RETURN_ADDR:
-    case BUILT_IN_EH_RETURN:
-    case BUILT_IN_EH_RETURN_DATA_REGNO:
-    case BUILT_IN_EXTEND_POINTER:
-    case BUILT_IN_EH_POINTER:
-    case BUILT_IN_EH_FILTER:
-    case BUILT_IN_EH_COPY_VALUES:
-      return false;
-
-    case BUILT_IN_VA_START:
-    case BUILT_IN_VA_END:
-    case BUILT_IN_VA_COPY:
-    case BUILT_IN_EXPECT:
-    case BUILT_IN_EXPECT_WITH_PROBABILITY:
-    case BUILT_IN_ASSUME_ALIGNED:
-    case BUILT_IN_PREFETCH:
-      return false;
-
-    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
-    case BUILT_IN_OBJECT_SIZE:
-    case BUILT_IN_THREAD_POINTER:
-    case BUILT_IN_SET_THREAD_POINTER:
-      return false;
-
-    default:
-      return true;
-    }
-}
-#endif
-
 typedef hash_set<tree> indirect_parms_t;
 
 static tree
@@ -1402,32 +1398,61 @@ remove_named_attribute_unsharing (const char *name, tree *attrs)
     }
 }
 
-unsigned int
-pass_ipa_strub_mode::execute (function *)
+static int last_cgraph_order;
+
+static bool
+ipa_strub_set_mode_for_new_functions ()
 {
-  cgraph_node *onode;
+  if (last_cgraph_order && symtab->order == last_cgraph_order)
+    /* If we're called again after the first call,
+       then the first call must have returned true.  */
+    return true;
 
-  /* If no strub flag was given in the command line,
-     set the actual default.  */
-  if (flag_strub == -2)
-    flag_strub = 3; // for testing only; was: -1;
+  cgraph_node *node;
 
   bool any_strub = false;
 
+  /* Go through the functions twice, once over non-aliases, and then over
+     aliases, so that aliases can reuse the mode computation of their ultimate
+     targets.  */
   for (int aliases = 0; aliases <= 1; aliases++)
-    FOR_EACH_FUNCTION (onode)
+    FOR_EACH_FUNCTION (node)
     {
-      if (!onode->alias != !aliases)
+      if (!node->alias != !aliases)
+	continue;
+
+      /*  Already done.  */
+      if (node->order < last_cgraph_order)
 	continue;
 
-      enum strub_mode mode = set_strub_mode (onode);
+      enum strub_mode mode = set_strub_mode (node);
 
       if (mode == STRUB_AT_CALLS || mode == STRUB_INTERNAL)
 	any_strub = true;
     }
 
+  if (any_strub)
+    last_cgraph_order = symtab->order;
+
+  return any_strub;
+}
+
+unsigned int
+pass_ipa_strub_mode::execute (function *)
+{
+  /* If no strub flag was given in the command line,
+     set the actual default.  */
+  if (flag_strub == -2)
+    flag_strub = 3; // for testing only; was: -1;
+
+  last_cgraph_order = 0;
+  bool any_strub = ipa_strub_set_mode_for_new_functions ();
+
   if (!any_strub)
     flag_strub = 0;
+  else
+    /* Verify before any inlining or other transformations.  */
+    verify_strub ();
 
   return 0;
 }
@@ -1438,54 +1463,13 @@ make_pass_ipa_strub_mode (gcc::context *ctxt)
   return new pass_ipa_strub_mode (ctxt);
 }
 
-/* Check that strub functions don't call non-strub functions, and that
-   always_inline strub functions are only called by strub
-   functions.  */
-static void
-verify_strub ()
-{
-  cgraph_node *node;
-
-  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
-  {
-    enum strub_mode caller_mode = get_strub_mode (node);
-    bool strub_context
-      = (caller_mode == STRUB_AT_CALLS
-	 || caller_mode == STRUB_WRAPPED
-	 || caller_mode == STRUB_INLINABLE);
-
-    for (cgraph_edge *e = node->callees; e; e = e->next_callee)
-      if (e->indirect_unknown_callee)
-	{
-	  if (!strub_context)
-	    continue;
-
-	  tree callee_fntype = gimple_call_fntype (e->call_stmt);
-	  enum strub_mode callee_mode
-	    = get_strub_mode_from_type (callee_fntype);
-
-	  if (callee_mode == STRUB_DISABLED
-	      || callee_mode == STRUB_INTERNAL)
-	    error_at (gimple_location (e->call_stmt),
-		      "indirect non-strub call in strub context %qD",
-		      node->decl);
-	}
-      else if (!strub_callable_from_p (e->callee, node))
-	error_at (gimple_location (e->call_stmt),
-		  "calling non-strub %qD in strub context %qD",
-		  e->callee->decl, node->decl);
-  }
-
-  /* ??? Check strub-wise pointer type compatibility of variables and
-     functions, or is this already taken care of on account of the
-     attribute's being marked as affecting type identity?  */
-}
-
 unsigned int
 pass_ipa_strub::execute (function *)
 {
   cgraph_node *onode;
 
+  ipa_strub_set_mode_for_new_functions ();
+
   FOR_EACH_FUNCTION (onode)
   {
     enum strub_mode mode = get_strub_mode (onode);
@@ -1572,7 +1556,7 @@ pass_ipa_strub::execute (function *)
 	      // Mostly copied from gimple_call_copy_skip_args.
 	      int i = 0;
 	      int nargs = gimple_call_num_args (stmt);
-	      auto_vec<tree> vargs (nargs + 1);
+	      auto_vec<tree> vargs (MAX (nargs, named_args) + 1);
 	      gcall *new_stmt;
 
 	      /* pr71109.c calls a prototypeless function, then defines it with
@@ -1630,15 +1614,47 @@ pass_ipa_strub::execute (function *)
 	    gimple_seq seq = NULL;
 
 	    {
-#if 0
-	      tree lswm = create_tmp_var (ptr_type_node, ".L.strub.watermark");
-	      gassign *load = gimple_build_assign (lswm, swm);
-	      gimple_seq_add_stmt (&seq, load);
-#else
-	      tree lswm = unshare_expr (swmp);
+#if !ATTR_FNSPEC_DECONST_WATERMARK
+	      /* If the call will be assumed to not modify or even read the
+		 watermark, make it read and modified ourselves.  */
+	      if ((gimple_call_flags (wrcall)
+		   & (ECF_CONST | ECF_PURE | ECF_NOVOPS)))
+		{
+		  vec<tree, va_gc> *inputs = NULL;
+		  vec<tree, va_gc> *outputs = NULL;
+		  vec_safe_push (outputs,
+				 build_tree_list
+				 (build_tree_list
+				  (NULL_TREE, build_string (2, "=m")),
+				  swm));
+		  vec_safe_push (inputs,
+				 build_tree_list
+				 (build_tree_list
+				  (NULL_TREE, build_string (1, "m")),
+				  swm));
+		  gasm *forcemod = gimple_build_asm_vec ("", inputs, outputs,
+							 NULL, NULL);
+		  gimple_seq_add_stmt (&seq, forcemod);
+
+		  /* If the call will be assumed to not even read the watermark,
+		     make sure it is already in memory before the call.  */
+		  if ((gimple_call_flags (wrcall) & ECF_CONST))
+		    {
+		      vec<tree, va_gc> *inputs = NULL;
+		      vec_safe_push (inputs,
+				     build_tree_list
+				     (build_tree_list
+				      (NULL_TREE, build_string (1, "m")),
+				      swm));
+		      gasm *force_store = gimple_build_asm_vec ("", inputs, NULL,
+								NULL, NULL);
+		      gsi_insert_before (&gsi, force_store, GSI_SAME_STMT);
+		    }
+		}
 #endif
 
-	      gcall *sleave = gimple_build_call (get_leave (), 1, lswm);
+	      gcall *sleave = gimple_build_call (get_leave (), 1,
+						 unshare_expr (swmp));
 	      gimple_seq_add_stmt (&seq, sleave);
 
 	      gassign *clobber = gimple_build_assign (swm,
@@ -1652,6 +1668,50 @@ pass_ipa_strub::execute (function *)
 	    pop_cfun ();
 	  }
 
+#if ATTR_FNSPEC_DECONST_WATERMARK
+	{
+	  int flags = flags_from_decl_or_type (onode->decl);
+	  tree fnspec = lookup_attribute ("fn spec", TREE_TYPE (onode->decl));
+
+	  if ((flags & (ECF_CONST | ECF_PURE | ECF_NOVOPS)) || fnspec)
+	    {
+	      size_t xargs = 1;
+	      size_t curlen = 0, tgtlen = 2 + 2 * (named_args + xargs);
+	      auto_vec<char> nspecv (tgtlen);
+	      char *nspec = &nspecv[0]; /* It will *not* be NUL-terminated!  */
+	      if (fnspec)
+		{
+		  tree fnspecstr = TREE_VALUE (TREE_VALUE (fnspec));
+		  curlen = TREE_STRING_LENGTH (fnspecstr);
+		  memcpy (nspec, TREE_STRING_POINTER (fnspecstr), curlen);
+		}
+	      if (!curlen)
+		{
+		  nspec[curlen++] = '.';
+		  nspec[curlen++] = ((flags & ECF_CONST)
+				     ? 'c'
+				     : (flags & ECF_PURE)
+				     ? 'p'
+				     : ' ');
+		}
+	      while (curlen < tgtlen - 2 * xargs)
+		{
+		  nspec[curlen++] = '.';
+		  nspec[curlen++] = ' ';
+		}
+	      nspec[curlen++] = 'W';
+	      nspec[curlen++] = 't';
+
+	      /* The type has already been copied before adding parameters.  */
+	      TYPE_ATTRIBUTES (TREE_TYPE (onode->decl))
+		= tree_cons (get_identifier ("fn spec"),
+			     build_tree_list (NULL_TREE,
+					      build_string (tgtlen, nspec)),
+			     TYPE_ATTRIBUTES (TREE_TYPE (onode->decl)));
+	    }
+	}
+#endif
+
 	if (!onode->has_gimple_body_p ())
 	  continue;
 
@@ -1807,11 +1867,13 @@ pass_ipa_strub::execute (function *)
        anyway, but performed at the caller.  */
     indirect_parms_t indirect_nparms (3, false);
     unsigned adjust_ftype = 0;
+    unsigned named_args = 0;
     for (tree parm = DECL_ARGUMENTS (onode->decl),
 	   nparm = DECL_ARGUMENTS (nnode->decl),
 	   nparmt = TYPE_ARG_TYPES (TREE_TYPE (nnode->decl));
 	 parm;
-	 parm = DECL_CHAIN (parm),
+	 named_args++,
+	   parm = DECL_CHAIN (parm),
 	   nparm = DECL_CHAIN (nparm),
 	   nparmt = nparmt ? TREE_CHAIN (nparmt) : NULL_TREE)
       if (!(0 /* DECL_BY_REFERENCE (narg) */
@@ -1840,7 +1902,11 @@ pass_ipa_strub::execute (function *)
 	  relayout_decl (nparm);
 	  TREE_ADDRESSABLE (nparm) = 0;
 	  DECL_BY_REFERENCE (nparm) = 0;
+#if FOR_GCC_11P
 	  DECL_NOT_GIMPLE_REG_P (nparm) = 0;
+#else
+	  DECL_GIMPLE_REG_P (nparm) = 1;
+#endif
 	  /* ??? This avoids mismatches in debug info bind stmts in
 	     e.g. a-chahan .  */
 	  DECL_ABSTRACT_ORIGIN (nparm) = NULL;
@@ -1860,6 +1926,7 @@ pass_ipa_strub::execute (function *)
 	gcc_checking_assert (TYPE_ARG_TYPES (nftype)
 			     != TYPE_ARG_TYPES (TREE_TYPE (onode->decl)));
 
+#if HAVE_ATTR_FNSPEC
 	/* Check that fnspec still works for the modified function signature,
 	   and drop it otherwise.  */
 	bool drop_fnspec = false;
@@ -1871,15 +1938,20 @@ pass_ipa_strub::execute (function *)
 	  retcopy = (unsigned) -1;
 
 	unsigned i = 0;
+#endif
 	for (tree nparm = DECL_ARGUMENTS (nnode->decl),
 	       nparmt = TYPE_ARG_TYPES (nftype);
 	     adjust_ftype > 0;
-	     nparm = DECL_CHAIN (nparm), nparmt = TREE_CHAIN (nparmt), i++)
+#if HAVE_ATTR_FNSPEC
+	     i++,
+#endif
+	       nparm = DECL_CHAIN (nparm), nparmt = TREE_CHAIN (nparmt))
 	  if (indirect_nparms.contains (nparm))
 	    {
 	      TREE_VALUE (nparmt) = TREE_TYPE (nparm);
 	      adjust_ftype--;
 
+#if HAVE_ATTR_FNSPEC
 	      if (fnspec && !drop_fnspec)
 		{
 		  if (i == retcopy)
@@ -1907,16 +1979,93 @@ pass_ipa_strub::execute (function *)
 			drop_fnspec = true;
 		    }
 		}
+#endif
 	    }
 
+#if HAVE_ATTR_FNSPEC
 	/* ??? Maybe we could adjust it instead.  */
 	if (drop_fnspec)
 	  remove_named_attribute_unsharing ("fn spec",
 					    &TYPE_ATTRIBUTES (nftype));
+#endif
 
 	TREE_TYPE (nnode->decl) = nftype;
       }
 
+#if ATTR_FNSPEC_DECONST_WATERMARK
+    {
+      int flags = flags_from_decl_or_type (nnode->decl);
+      tree fnspec = lookup_attribute ("fn spec", TREE_TYPE (nnode->decl));
+
+      if ((flags & (ECF_CONST | ECF_PURE | ECF_NOVOPS)) || fnspec)
+	{
+	  size_t xargs = 1 + int (is_stdarg) + int (apply_args);
+	  size_t curlen = 0, tgtlen = 2 + 2 * (named_args + xargs);
+	  auto_vec<char> nspecv (tgtlen);
+	  char *nspec = &nspecv[0]; /* It will *not* be NUL-terminated!  */
+	  bool no_writes_p = true;
+	  if (fnspec)
+	    {
+	      tree fnspecstr = TREE_VALUE (TREE_VALUE (fnspec));
+	      curlen = TREE_STRING_LENGTH (fnspecstr);
+	      memcpy (nspec, TREE_STRING_POINTER (fnspecstr), curlen);
+	      if (!(flags & (ECF_CONST | ECF_PURE | ECF_NOVOPS))
+		  && curlen >= 2
+		  && nspec[1] != 'c' && nspec[1] != 'C'
+		  && nspec[1] != 'p' && nspec[1] != 'P')
+		no_writes_p = false;
+	    }
+	  if (!curlen)
+	    {
+	      nspec[curlen++] = '.';
+	      nspec[curlen++] = ((flags & ECF_CONST)
+				 ? 'c'
+				 : (flags & ECF_PURE)
+				 ? 'p'
+				 : ' ');
+	    }
+	  while (curlen < tgtlen - 2 * xargs)
+	    {
+	      nspec[curlen++] = '.';
+	      nspec[curlen++] = ' ';
+	    }
+
+	  /* These extra args are unlikely to be present in const or pure
+	     functions.  It's conceivable that a function that takes variable
+	     arguments, or that passes its arguments on to another function,
+	     could be const or pure, but it would not modify the arguments, and,
+	     being pure or const, it couldn't possibly modify or even access
+	     memory referenced by them.  But it can read from these internal
+	     data structures created by the wrapper, and from any
+	     argument-passing memory referenced by them, so we denote the
+	     possibility of reading from multiple levels of indirection, but
+	     only of reading because const/pure.  */
+	  if (apply_args)
+	    {
+	      nspec[curlen++] = (no_writes_p ? 'r' : '.');
+	      nspec[curlen++] = (no_writes_p ? 't' : ' ');
+	    }
+	  if (is_stdarg)
+	    {
+	      nspec[curlen++] = (no_writes_p ? 'r' : '.');
+	      nspec[curlen++] = (no_writes_p ? 't' : ' ');
+	    }
+
+	  nspec[curlen++] = 'W';
+	  nspec[curlen++] = 't';
+
+	  /* The type has already been copied before adding parameters.  */
+	  gcc_checking_assert (TYPE_ARG_TYPES (TREE_TYPE (nnode->decl))
+			       != TYPE_ARG_TYPES (TREE_TYPE (onode->decl)));
+	  TYPE_ATTRIBUTES (TREE_TYPE (nnode->decl))
+	    = tree_cons (get_identifier ("fn spec"),
+			 build_tree_list (NULL_TREE,
+					  build_string (tgtlen, nspec)),
+			 TYPE_ATTRIBUTES (TREE_TYPE (nnode->decl)));
+	}
+    }
+#endif
+
     {
       tree decl = onode->decl;
       cgraph_node *target = nnode;
@@ -1940,8 +2089,14 @@ pass_ipa_strub::execute (function *)
 	/* Turn alias into thunk and expand it into GIMPLE representation.  */
 	onode->definition = true;
 
+#if FOR_GCC_11P
 	thunk_info::get_create (onode);
 	onode->thunk = true;
+#else
+	memset (&onode->thunk, 0, sizeof (cgraph_thunk_info));
+	onode->thunk.thunk_p = true;
+	onode->thunk.alias = target->decl;
+#endif
 #if !IMPLICIT_CGRAPH_EDGES
 	onode->create_edge (target, NULL, onode->count);
 #endif
@@ -1980,9 +2135,11 @@ pass_ipa_strub::execute (function *)
 
 	    current_function_decl = thunk_fndecl;
 
+#if FOR_GCC_11P
 	    /* Ensure thunks are emitted in their correct sections.  */
 	    resolve_unique_section (thunk_fndecl, 0,
 				    flag_function_sections);
+#endif
 
 	    bitmap_obstack_initialize (NULL);
 
@@ -2066,8 +2223,14 @@ pass_ipa_strub::execute (function *)
 		      tree addr = build1 (ADDR_EXPR, ref_type, arg);
 		      tmp = arg = addr;
 		    }
+#if ! FOR_GCC_11P
+		  else if (VECTOR_TYPE_P (TREE_TYPE (arg))
+			   || TREE_CODE (TREE_TYPE (arg)) == COMPLEX_TYPE)
+		    DECL_GIMPLE_REG_P (arg) = 1;
+#else
 		  else
 		    DECL_NOT_GIMPLE_REG_P (arg) = 0;
+#endif
 
 		  /* Convert the argument back to the type used by the calling
 		     conventions, e.g. a non-prototyped float type is passed as
@@ -2151,8 +2314,10 @@ pass_ipa_strub::execute (function *)
 	    profile_status_for_fn (cfun)
 	      = cfg_count.initialized_p () && cfg_count.ipa_p ()
 	      ? PROFILE_READ : PROFILE_GUESSED;
+#if FOR_GCC_11P
 	    /* FIXME: C++ FE should stop setting TREE_ASM_WRITTEN on thunks.  */
 	    // TREE_ASM_WRITTEN (thunk_fndecl) = false;
+#endif
 	    delete_unreachable_blocks ();
 	    update_ssa (TODO_update_ssa);
 	    checking_verify_flow_info ();
@@ -2160,7 +2325,11 @@ pass_ipa_strub::execute (function *)
 
 	    /* Since we want to emit the thunk, we explicitly mark its name as
 	       referenced.  */
+#if FOR_GCC_11P
 	    onode->thunk = false;
+#else
+	    onode->thunk.thunk_p = false;
+#endif
 	    onode->lowered = true;
 	    bitmap_obstack_release (NULL);
 	  }
@@ -2168,7 +2337,9 @@ pass_ipa_strub::execute (function *)
 	  set_cfun (NULL);
 	}
 
+#if FOR_GCC_11P
 	thunk_info::remove (onode);
+#endif
 
 	// some more of create_wrapper at the end of the next block.
       }
@@ -2381,15 +2552,47 @@ pass_ipa_strub::execute (function *)
       update_stmt (wrcall);
 
       {
-#if 0
-	tree lswm = create_tmp_var (ptr_type_node, ".L.strub.watermark");
-	gassign *load = gimple_build_assign (lswm, swm);
-	gimple_seq_add_stmt (&seq, load);
-#else
-	tree lswm = unshare_expr (swmp);
+#if !ATTR_FNSPEC_DECONST_WATERMARK
+	/* If the call will be assumed to not modify or even read the
+	   watermark, make it read and modified ourselves.  */
+	if ((gimple_call_flags (wrcall)
+	     & (ECF_CONST | ECF_PURE | ECF_NOVOPS)))
+	  {
+	    vec<tree, va_gc> *inputs = NULL;
+	    vec<tree, va_gc> *outputs = NULL;
+	    vec_safe_push (outputs,
+			   build_tree_list
+			   (build_tree_list
+			    (NULL_TREE, build_string (2, "=m")),
+			    swm));
+	    vec_safe_push (inputs,
+			   build_tree_list
+			   (build_tree_list
+			    (NULL_TREE, build_string (1, "m")),
+			    swm));
+	    gasm *forcemod = gimple_build_asm_vec ("", inputs, outputs,
+						   NULL, NULL);
+	    gimple_seq_add_stmt (&seq, forcemod);
+
+	    /* If the call will be assumed to not even read the watermark,
+	       make sure it is already in memory before the call.  */
+	    if ((gimple_call_flags (wrcall) & ECF_CONST))
+	      {
+		vec<tree, va_gc> *inputs = NULL;
+		vec_safe_push (inputs,
+			       build_tree_list
+			       (build_tree_list
+				(NULL_TREE, build_string (1, "m")),
+				swm));
+		gasm *force_store = gimple_build_asm_vec ("", inputs, NULL,
+							  NULL, NULL);
+		gsi_insert_before (&gsi, force_store, GSI_SAME_STMT);
+	      }
+	  }
 #endif
 
-	gcall *sleave = gimple_build_call (get_leave (), 1, lswm);
+	gcall *sleave = gimple_build_call (get_leave (), 1,
+					   unshare_expr (swmp));
 	gimple_seq_add_stmt (&seq, sleave);
 
 	gassign *clobber = gimple_build_assign (swm,
@@ -2418,7 +2621,14 @@ pass_ipa_strub::execute (function *)
 #endif
   }
 
-  verify_strub ();
+  if (flag_checking)
+    {
+      /* We've already verified before any inlining or other transformations.
+	 Recheck after strub transformations only if checking is enabled, since
+	 they should not introduce any incompatibilities.  */
+      ipa_strub_set_mode_for_new_functions ();
+      verify_strub ();
+    }
 
   return 0;
 }


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [gcc(refs/users/aoliva/heads/strub)] make builtins callable
@ 2021-07-26 21:05 Alexandre Oliva
  0 siblings, 0 replies; 7+ messages in thread
From: Alexandre Oliva @ 2021-07-26 21:05 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:9d2df29d248f215b9ca0ec7b493273ae20d75d2b

commit 9d2df29d248f215b9ca0ec7b493273ae20d75d2b
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Mon Jul 26 12:15:36 2021 -0300

    make builtins callable

Diff:
---
 gcc/ipa-strub.c | 311 ++++++++++++++++++++++++++------------------------------
 1 file changed, 142 insertions(+), 169 deletions(-)

diff --git a/gcc/ipa-strub.c b/gcc/ipa-strub.c
index 7b05321e980..7753f0207d7 100644
--- a/gcc/ipa-strub.c
+++ b/gcc/ipa-strub.c
@@ -438,6 +438,34 @@ strub_from_body_p (cgraph_node *node)
   return false;
 }
 
+/* Return true iff node is associated with a builtin that should be callable
+   from strub contexts.  */
+static inline bool
+strub_callable_builtin_p (cgraph_node *node)
+{
+  if (DECL_BUILT_IN_CLASS (node->decl) != BUILT_IN_NORMAL)
+    return false;
+
+  enum built_in_function fcode = DECL_FUNCTION_CODE (node->decl);
+
+  switch (fcode)
+    {
+    case BUILT_IN_NONE:
+      gcc_unreachable ();
+
+      /* ??? Make all builtins callable.  We wish to make any builtin call the
+	 compiler might introduce on its own callable.  Anything that is
+	 predictable enough as to be known not to allow stack data that should
+	 be strubbed to unintentionally escape to non-strub contexts can be
+	 allowed, and pretty much every builtin appears to fit this description.
+	 The exceptions to this rule seem to be rare, and only available as
+	 explicit __builtin calls, so let's keep it simple and allow all of
+	 them...  */
+    default:
+      return true;
+    }
+}
+
 static enum strub_mode
 compute_strub_mode (cgraph_node *node, tree strub_attr)
 {
@@ -495,7 +523,8 @@ compute_strub_mode (cgraph_node *node, tree strub_attr)
     = (!strub_flag_disabled
        && (strub_attr
 	   ? req_mode == STRUB_CALLABLE
-	   : strub_flag_viable));
+	   : (strub_flag_viable
+	      || strub_callable_builtin_p (node))));
 
   /* This is a shorthand for either strub-enabled mode.  */
   const bool consider_strub
@@ -730,6 +759,26 @@ set_strub_mode (cgraph_node *node)
 {
   tree attr = get_strub_attr_from_decl (node->decl);
 
+  if (attr)
+    switch (enum strub_mode mode = get_strub_mode_from_attr (attr))
+      {
+	/* These can't have been requested through user attributes, so we must
+	   have already gone through them.  */
+      case STRUB_WRAPPER:
+      case STRUB_WRAPPED:
+      case STRUB_INLINABLE:
+	return mode;
+
+      case STRUB_DISABLED:
+      case STRUB_AT_CALLS:
+      case STRUB_INTERNAL:
+      case STRUB_CALLABLE:
+	break;
+
+      default:
+	gcc_unreachable ();
+      }
+
   cgraph_node *xnode = node;
   if (node->alias)
     xnode = node->ultimate_alias_target ();
@@ -845,6 +894,49 @@ strub_inlinable_p (cgraph_node *callee, cgraph_node *caller)
   return false;
 }
 
+/* Check that strub functions don't call non-strub functions, and that
+   always_inline strub functions are only called by strub
+   functions.  */
+static void
+verify_strub ()
+{
+  cgraph_node *node;
+
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+  {
+    enum strub_mode caller_mode = get_strub_mode (node);
+    bool strub_context
+      = (caller_mode == STRUB_AT_CALLS
+	 || caller_mode == STRUB_WRAPPED
+	 || caller_mode == STRUB_INLINABLE);
+
+    for (cgraph_edge *e = node->callees; e; e = e->next_callee)
+      if (e->indirect_unknown_callee)
+	{
+	  if (!strub_context)
+	    continue;
+
+	  tree callee_fntype = gimple_call_fntype (e->call_stmt);
+	  enum strub_mode callee_mode
+	    = get_strub_mode_from_type (callee_fntype);
+
+	  if (callee_mode == STRUB_DISABLED
+	      || callee_mode == STRUB_INTERNAL)
+	    error_at (gimple_location (e->call_stmt),
+		      "indirect non-strub call in strub context %qD",
+		      node->decl);
+	}
+      else if (!strub_callable_from_p (e->callee, node))
+	error_at (gimple_location (e->call_stmt),
+		  "calling non-strub %qD in strub context %qD",
+		  e->callee->decl, node->decl);
+  }
+
+  /* ??? Check strub-wise pointer type compatibility of variables and
+     functions, or is this already taken care of on account of the
+     attribute's being marked as affecting type identity?  */
+}
+
 namespace {
 
 const pass_data pass_data_ipa_strub_mode = {
@@ -907,8 +999,6 @@ public:
 	   NULL, NULL);						\
 	TREE_NOTHROW (decl) = true;				\
 	set_builtin_decl ((CODE), decl, true);			\
-	set_strub_mode_to (cgraph_node::get_create (decl),	\
-			   STRUB_CALLABLE);			\
       }								\
     return decl;						\
   }
@@ -939,8 +1029,6 @@ public:
 	   "__strub_" #NAME, attrs);				\
 	TREE_NOTHROW (decl) = true;				\
 	set_builtin_decl ((CODE), decl, true);			\
-	set_strub_mode_to (cgraph_node::get_create (decl),	\
-			   STRUB_CALLABLE);			\
       }								\
     return decl;						\
   }
@@ -1014,116 +1102,6 @@ public:
 
 } // anon namespace
 
-#if 0
-static bool
-may_throw_p (gcall *stmt)
-{
-  return flag_exceptions && !gimple_call_nothrow_p (stmt);
-}
-
-static bool
-strub_this_call_p (gcall *stmt)
-{
-  if (gimple_call_internal_p (stmt))
-    return false;
-
-  /* If there's no outgoing path in which to do the scrubbing, don't
-     bother.  */
-  if (gimple_call_noreturn_p (stmt) && !may_throw_p (stmt))
-    return false;
-
-  /* ??? Maybe non-mandatory tail calls should be disabled for
-     scrubbing.  Or maybe it won't matter, as long as both tail-caller
-     and callee are scrubbing-capable.  */
-  if (gimple_call_must_tail_p (stmt) || gimple_call_tail_p (stmt))
-    return false;
-
-  if (gimple_alloca_call_p (stmt))
-    return true;
-
-  tree fndecl = gimple_call_fndecl (stmt);
-  if (!fndecl)
-    return true;
-
-  if (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
-    return true;
-
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-
-  switch (fcode)
-    {
-    CASE_BUILT_IN_ALLOCA:
-      return true;
-
-    case BUILT_IN_NONE:
-      return true;
-
-    case BUILT_IN___STRUB_ENTER:
-    case BUILT_IN___STRUB_UPDATE:
-    case BUILT_IN___STRUB_LEAVE:
-      return false;
-
-    case BUILT_IN_CLASSIFY_TYPE:
-    case BUILT_IN_CONSTANT_P:
-      return false;
-
-    case BUILT_IN_RETURN_ADDRESS:
-    case BUILT_IN_FRAME_ADDRESS:
-    case BUILT_IN_STACK_ADDRESS:
-    case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
-      return false;
-
-    case BUILT_IN_STACK_SAVE:
-    case BUILT_IN_STACK_RESTORE:
-    case BUILT_IN_ASAN_ALLOCAS_UNPOISON:
-      return false;
-
-    case BUILT_IN_SETJMP_SETUP:
-    case BUILT_IN_SETJMP_RECEIVER:
-    case BUILT_IN_LONGJMP:
-    case BUILT_IN_NONLOCAL_GOTO:
-    case BUILT_IN_UPDATE_SETJMP_BUF:
-    case BUILT_IN_TRAP:
-    case BUILT_IN_UNREACHABLE:
-      return false;
-
-    case BUILT_IN_UNWIND_INIT:
-    case BUILT_IN_DWARF_CFA:
-#ifdef DWARF2_UNWIND_INFO
-    case BUILT_IN_DWARF_SP_COLUMN:
-    case BUILT_IN_INIT_DWARF_REG_SIZES:
-#endif
-    case BUILT_IN_FROB_RETURN_ADDR:
-    case BUILT_IN_EXTRACT_RETURN_ADDR:
-    case BUILT_IN_EH_RETURN:
-    case BUILT_IN_EH_RETURN_DATA_REGNO:
-    case BUILT_IN_EXTEND_POINTER:
-    case BUILT_IN_EH_POINTER:
-    case BUILT_IN_EH_FILTER:
-    case BUILT_IN_EH_COPY_VALUES:
-      return false;
-
-    case BUILT_IN_VA_START:
-    case BUILT_IN_VA_END:
-    case BUILT_IN_VA_COPY:
-    case BUILT_IN_EXPECT:
-    case BUILT_IN_EXPECT_WITH_PROBABILITY:
-    case BUILT_IN_ASSUME_ALIGNED:
-    case BUILT_IN_PREFETCH:
-      return false;
-
-    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
-    case BUILT_IN_OBJECT_SIZE:
-    case BUILT_IN_THREAD_POINTER:
-    case BUILT_IN_SET_THREAD_POINTER:
-      return false;
-
-    default:
-      return true;
-    }
-}
-#endif
-
 typedef hash_set<tree> indirect_parms_t;
 
 static tree
@@ -1402,32 +1380,61 @@ remove_named_attribute_unsharing (const char *name, tree *attrs)
     }
 }
 
-unsigned int
-pass_ipa_strub_mode::execute (function *)
+static int last_cgraph_order;
+
+static bool
+ipa_strub_set_mode_for_new_functions ()
 {
-  cgraph_node *onode;
+  if (last_cgraph_order && symtab->order == last_cgraph_order)
+    /* If we're called again after the first call,
+       then the first call must have returned true.  */
+    return true;
 
-  /* If no strub flag was given in the command line,
-     set the actual default.  */
-  if (flag_strub == -2)
-    flag_strub = 3; // for testing only; was: -1;
+  cgraph_node *node;
 
   bool any_strub = false;
 
+  /* Go through the functions twice, once over non-aliases, and then over
+     aliases, so that aliases can reuse the mode computation of their ultimate
+     targets.  */
   for (int aliases = 0; aliases <= 1; aliases++)
-    FOR_EACH_FUNCTION (onode)
+    FOR_EACH_FUNCTION (node)
     {
-      if (!onode->alias != !aliases)
+      if (!node->alias != !aliases)
 	continue;
 
-      enum strub_mode mode = set_strub_mode (onode);
+      /*  Already done.  */
+      if (node->order < last_cgraph_order)
+	continue;
+
+      enum strub_mode mode = set_strub_mode (node);
 
       if (mode == STRUB_AT_CALLS || mode == STRUB_INTERNAL)
 	any_strub = true;
     }
 
+  if (any_strub)
+    last_cgraph_order = symtab->order;
+
+  return any_strub;
+}
+
+unsigned int
+pass_ipa_strub_mode::execute (function *)
+{
+  /* If no strub flag was given in the command line,
+     set the actual default.  */
+  if (flag_strub == -2)
+    flag_strub = 3; // for testing only; was: -1;
+
+  last_cgraph_order = 0;
+  bool any_strub = ipa_strub_set_mode_for_new_functions ();
+
   if (!any_strub)
     flag_strub = 0;
+  else
+    /* Verify before any inlining or other transformations.  */
+    verify_strub ();
 
   return 0;
 }
@@ -1438,54 +1445,13 @@ make_pass_ipa_strub_mode (gcc::context *ctxt)
   return new pass_ipa_strub_mode (ctxt);
 }
 
-/* Check that strub functions don't call non-strub functions, and that
-   always_inline strub functions are only called by strub
-   functions.  */
-static void
-verify_strub ()
-{
-  cgraph_node *node;
-
-  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
-  {
-    enum strub_mode caller_mode = get_strub_mode (node);
-    bool strub_context
-      = (caller_mode == STRUB_AT_CALLS
-	 || caller_mode == STRUB_WRAPPED
-	 || caller_mode == STRUB_INLINABLE);
-
-    for (cgraph_edge *e = node->callees; e; e = e->next_callee)
-      if (e->indirect_unknown_callee)
-	{
-	  if (!strub_context)
-	    continue;
-
-	  tree callee_fntype = gimple_call_fntype (e->call_stmt);
-	  enum strub_mode callee_mode
-	    = get_strub_mode_from_type (callee_fntype);
-
-	  if (callee_mode == STRUB_DISABLED
-	      || callee_mode == STRUB_INTERNAL)
-	    error_at (gimple_location (e->call_stmt),
-		      "indirect non-strub call in strub context %qD",
-		      node->decl);
-	}
-      else if (!strub_callable_from_p (e->callee, node))
-	error_at (gimple_location (e->call_stmt),
-		  "calling non-strub %qD in strub context %qD",
-		  e->callee->decl, node->decl);
-  }
-
-  /* ??? Check strub-wise pointer type compatibility of variables and
-     functions, or is this already taken care of on account of the
-     attribute's being marked as affecting type identity?  */
-}
-
 unsigned int
 pass_ipa_strub::execute (function *)
 {
   cgraph_node *onode;
 
+  ipa_strub_set_mode_for_new_functions ();
+
   FOR_EACH_FUNCTION (onode)
   {
     enum strub_mode mode = get_strub_mode (onode);
@@ -2418,7 +2384,14 @@ pass_ipa_strub::execute (function *)
 #endif
   }
 
-  verify_strub ();
+  if (flag_checking)
+    {
+      /* We've already verified before any inlining or other transformations.
+	 Recheck after strub transformations only if checking is enabled, since
+	 they should not introduce any incompatibilities.  */
+      ipa_strub_set_mode_for_new_functions ();
+      verify_strub ();
+    }
 
   return 0;
 }


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [gcc(refs/users/aoliva/heads/strub)] make builtins callable
@ 2021-07-26 20:55 Alexandre Oliva
  0 siblings, 0 replies; 7+ messages in thread
From: Alexandre Oliva @ 2021-07-26 20:55 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:8ef03efec294a58d6ba2b8328c3363a34afa03a8

commit 8ef03efec294a58d6ba2b8328c3363a34afa03a8
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Mon Jul 26 12:15:36 2021 -0300

    make builtins callable

Diff:
---
 gcc/ipa-strub.c | 318 ++++++++++++++++++++++++++------------------------------
 1 file changed, 148 insertions(+), 170 deletions(-)

diff --git a/gcc/ipa-strub.c b/gcc/ipa-strub.c
index 7b05321e980..573596edabd 100644
--- a/gcc/ipa-strub.c
+++ b/gcc/ipa-strub.c
@@ -438,6 +438,34 @@ strub_from_body_p (cgraph_node *node)
   return false;
 }
 
+/* Return true iff node is associated with a builtin that should be callable
+   from strub contexts.  */
+static inline bool
+strub_callable_builtin_p (cgraph_node *node)
+{
+  if (DECL_BUILT_IN_CLASS (node->decl) != BUILT_IN_NORMAL)
+    return false;
+
+  enum built_in_function fcode = DECL_FUNCTION_CODE (node->decl);
+
+  switch (fcode)
+    {
+    case BUILT_IN_NONE:
+      gcc_unreachable ();
+
+      /* ??? Make all builtins callable.  We wish to make any builtin call the
+	 compiler might introduce on its own callable.  Anything that is
+	 predictable enough as to be known not to allow stack data that should
+	 be strubbed to unintentionally escape to non-strub contexts can be
+	 allowed, and pretty much every builtin appears to fit this description.
+	 The exceptions to this rule seem to be rare, and only available as
+	 explicit __builtin calls, so let's keep it simple and allow all of
+	 them...  */
+    default:
+      return true;
+    }
+}
+
 static enum strub_mode
 compute_strub_mode (cgraph_node *node, tree strub_attr)
 {
@@ -495,7 +523,8 @@ compute_strub_mode (cgraph_node *node, tree strub_attr)
     = (!strub_flag_disabled
        && (strub_attr
 	   ? req_mode == STRUB_CALLABLE
-	   : strub_flag_viable));
+	   : (strub_flag_viable
+	      || strub_callable_builtin_p (node))));
 
   /* This is a shorthand for either strub-enabled mode.  */
   const bool consider_strub
@@ -730,6 +759,26 @@ set_strub_mode (cgraph_node *node)
 {
   tree attr = get_strub_attr_from_decl (node->decl);
 
+  if (attr)
+    switch (enum strub_mode mode = get_strub_mode_from_attr (attr))
+      {
+	/* These can't have been requested through user attributes, so we must
+	   have already gone through them.  */
+      case STRUB_WRAPPER:
+      case STRUB_WRAPPED:
+      case STRUB_INLINABLE:
+	return mode;
+
+      case STRUB_DISABLED:
+      case STRUB_AT_CALLS:
+      case STRUB_INTERNAL:
+      case STRUB_CALLABLE:
+	break;
+
+      default:
+	gcc_unreachable ();
+      }
+
   cgraph_node *xnode = node;
   if (node->alias)
     xnode = node->ultimate_alias_target ();
@@ -845,6 +894,49 @@ strub_inlinable_p (cgraph_node *callee, cgraph_node *caller)
   return false;
 }
 
+/* Check that strub functions don't call non-strub functions, and that
+   always_inline strub functions are only called by strub
+   functions.  */
+static void
+verify_strub ()
+{
+  cgraph_node *node;
+
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+  {
+    enum strub_mode caller_mode = get_strub_mode (node);
+    bool strub_context
+      = (caller_mode == STRUB_AT_CALLS
+	 || caller_mode == STRUB_WRAPPED
+	 || caller_mode == STRUB_INLINABLE);
+
+    for (cgraph_edge *e = node->callees; e; e = e->next_callee)
+      if (e->indirect_unknown_callee)
+	{
+	  if (!strub_context)
+	    continue;
+
+	  tree callee_fntype = gimple_call_fntype (e->call_stmt);
+	  enum strub_mode callee_mode
+	    = get_strub_mode_from_type (callee_fntype);
+
+	  if (callee_mode == STRUB_DISABLED
+	      || callee_mode == STRUB_INTERNAL)
+	    error_at (gimple_location (e->call_stmt),
+		      "indirect non-strub call in strub context %qD",
+		      node->decl);
+	}
+      else if (!strub_callable_from_p (e->callee, node))
+	error_at (gimple_location (e->call_stmt),
+		  "calling non-strub %qD in strub context %qD",
+		  e->callee->decl, node->decl);
+  }
+
+  /* ??? Check strub-wise pointer type compatibility of variables and
+     functions, or is this already taken care of on account of the
+     attribute's being marked as affecting type identity?  */
+}
+
 namespace {
 
 const pass_data pass_data_ipa_strub_mode = {
@@ -907,8 +999,6 @@ public:
 	   NULL, NULL);						\
 	TREE_NOTHROW (decl) = true;				\
 	set_builtin_decl ((CODE), decl, true);			\
-	set_strub_mode_to (cgraph_node::get_create (decl),	\
-			   STRUB_CALLABLE);			\
       }								\
     return decl;						\
   }
@@ -939,8 +1029,6 @@ public:
 	   "__strub_" #NAME, attrs);				\
 	TREE_NOTHROW (decl) = true;				\
 	set_builtin_decl ((CODE), decl, true);			\
-	set_strub_mode_to (cgraph_node::get_create (decl),	\
-			   STRUB_CALLABLE);			\
       }								\
     return decl;						\
   }
@@ -1014,116 +1102,6 @@ public:
 
 } // anon namespace
 
-#if 0
-static bool
-may_throw_p (gcall *stmt)
-{
-  return flag_exceptions && !gimple_call_nothrow_p (stmt);
-}
-
-static bool
-strub_this_call_p (gcall *stmt)
-{
-  if (gimple_call_internal_p (stmt))
-    return false;
-
-  /* If there's no outgoing path in which to do the scrubbing, don't
-     bother.  */
-  if (gimple_call_noreturn_p (stmt) && !may_throw_p (stmt))
-    return false;
-
-  /* ??? Maybe non-mandatory tail calls should be disabled for
-     scrubbing.  Or maybe it won't matter, as long as both tail-caller
-     and callee are scrubbing-capable.  */
-  if (gimple_call_must_tail_p (stmt) || gimple_call_tail_p (stmt))
-    return false;
-
-  if (gimple_alloca_call_p (stmt))
-    return true;
-
-  tree fndecl = gimple_call_fndecl (stmt);
-  if (!fndecl)
-    return true;
-
-  if (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
-    return true;
-
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-
-  switch (fcode)
-    {
-    CASE_BUILT_IN_ALLOCA:
-      return true;
-
-    case BUILT_IN_NONE:
-      return true;
-
-    case BUILT_IN___STRUB_ENTER:
-    case BUILT_IN___STRUB_UPDATE:
-    case BUILT_IN___STRUB_LEAVE:
-      return false;
-
-    case BUILT_IN_CLASSIFY_TYPE:
-    case BUILT_IN_CONSTANT_P:
-      return false;
-
-    case BUILT_IN_RETURN_ADDRESS:
-    case BUILT_IN_FRAME_ADDRESS:
-    case BUILT_IN_STACK_ADDRESS:
-    case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
-      return false;
-
-    case BUILT_IN_STACK_SAVE:
-    case BUILT_IN_STACK_RESTORE:
-    case BUILT_IN_ASAN_ALLOCAS_UNPOISON:
-      return false;
-
-    case BUILT_IN_SETJMP_SETUP:
-    case BUILT_IN_SETJMP_RECEIVER:
-    case BUILT_IN_LONGJMP:
-    case BUILT_IN_NONLOCAL_GOTO:
-    case BUILT_IN_UPDATE_SETJMP_BUF:
-    case BUILT_IN_TRAP:
-    case BUILT_IN_UNREACHABLE:
-      return false;
-
-    case BUILT_IN_UNWIND_INIT:
-    case BUILT_IN_DWARF_CFA:
-#ifdef DWARF2_UNWIND_INFO
-    case BUILT_IN_DWARF_SP_COLUMN:
-    case BUILT_IN_INIT_DWARF_REG_SIZES:
-#endif
-    case BUILT_IN_FROB_RETURN_ADDR:
-    case BUILT_IN_EXTRACT_RETURN_ADDR:
-    case BUILT_IN_EH_RETURN:
-    case BUILT_IN_EH_RETURN_DATA_REGNO:
-    case BUILT_IN_EXTEND_POINTER:
-    case BUILT_IN_EH_POINTER:
-    case BUILT_IN_EH_FILTER:
-    case BUILT_IN_EH_COPY_VALUES:
-      return false;
-
-    case BUILT_IN_VA_START:
-    case BUILT_IN_VA_END:
-    case BUILT_IN_VA_COPY:
-    case BUILT_IN_EXPECT:
-    case BUILT_IN_EXPECT_WITH_PROBABILITY:
-    case BUILT_IN_ASSUME_ALIGNED:
-    case BUILT_IN_PREFETCH:
-      return false;
-
-    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
-    case BUILT_IN_OBJECT_SIZE:
-    case BUILT_IN_THREAD_POINTER:
-    case BUILT_IN_SET_THREAD_POINTER:
-      return false;
-
-    default:
-      return true;
-    }
-}
-#endif
-
 typedef hash_set<tree> indirect_parms_t;
 
 static tree
@@ -1402,32 +1380,61 @@ remove_named_attribute_unsharing (const char *name, tree *attrs)
     }
 }
 
-unsigned int
-pass_ipa_strub_mode::execute (function *)
+static int last_cgraph_order;
+
+static bool
+ipa_strub_set_mode_for_new_functions ()
 {
-  cgraph_node *onode;
+  if (last_cgraph_order && symtab->order == last_cgraph_order)
+    /* If we're called again after the first call,
+       then the first call must have returned true.  */
+    return true;
 
-  /* If no strub flag was given in the command line,
-     set the actual default.  */
-  if (flag_strub == -2)
-    flag_strub = 3; // for testing only; was: -1;
+  cgraph_node *node;
 
   bool any_strub = false;
 
+  /* Go through the functions twice, once over non-aliases, and then over
+     aliases, so that aliases can reuse the mode computation of their ultimate
+     targets.  */
   for (int aliases = 0; aliases <= 1; aliases++)
-    FOR_EACH_FUNCTION (onode)
+    FOR_EACH_FUNCTION (node)
     {
-      if (!onode->alias != !aliases)
+      if (!node->alias != !aliases)
+	continue;
+
+      /*  Already done.  */
+      if (node->order < last_cgraph_order)
 	continue;
 
-      enum strub_mode mode = set_strub_mode (onode);
+      enum strub_mode mode = set_strub_mode (node);
 
       if (mode == STRUB_AT_CALLS || mode == STRUB_INTERNAL)
 	any_strub = true;
     }
 
+  if (any_strub)
+    last_cgraph_order = symtab->order;
+
+  return any_strub;
+}
+
+unsigned int
+pass_ipa_strub_mode::execute (function *)
+{
+  /* If no strub flag was given in the command line,
+     set the actual default.  */
+  if (flag_strub == -2)
+    flag_strub = 3; // for testing only; was: -1;
+
+  last_cgraph_order = 0;
+  bool any_strub = ipa_strub_set_mode_for_new_functions ();
+
   if (!any_strub)
     flag_strub = 0;
+  else
+    /* Verify before any inlining or other transformations.  */
+    verify_strub ();
 
   return 0;
 }
@@ -1438,54 +1445,13 @@ make_pass_ipa_strub_mode (gcc::context *ctxt)
   return new pass_ipa_strub_mode (ctxt);
 }
 
-/* Check that strub functions don't call non-strub functions, and that
-   always_inline strub functions are only called by strub
-   functions.  */
-static void
-verify_strub ()
-{
-  cgraph_node *node;
-
-  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
-  {
-    enum strub_mode caller_mode = get_strub_mode (node);
-    bool strub_context
-      = (caller_mode == STRUB_AT_CALLS
-	 || caller_mode == STRUB_WRAPPED
-	 || caller_mode == STRUB_INLINABLE);
-
-    for (cgraph_edge *e = node->callees; e; e = e->next_callee)
-      if (e->indirect_unknown_callee)
-	{
-	  if (!strub_context)
-	    continue;
-
-	  tree callee_fntype = gimple_call_fntype (e->call_stmt);
-	  enum strub_mode callee_mode
-	    = get_strub_mode_from_type (callee_fntype);
-
-	  if (callee_mode == STRUB_DISABLED
-	      || callee_mode == STRUB_INTERNAL)
-	    error_at (gimple_location (e->call_stmt),
-		      "indirect non-strub call in strub context %qD",
-		      node->decl);
-	}
-      else if (!strub_callable_from_p (e->callee, node))
-	error_at (gimple_location (e->call_stmt),
-		  "calling non-strub %qD in strub context %qD",
-		  e->callee->decl, node->decl);
-  }
-
-  /* ??? Check strub-wise pointer type compatibility of variables and
-     functions, or is this already taken care of on account of the
-     attribute's being marked as affecting type identity?  */
-}
-
 unsigned int
 pass_ipa_strub::execute (function *)
 {
   cgraph_node *onode;
 
+  ipa_strub_set_mode_for_new_functions ();
+
   FOR_EACH_FUNCTION (onode)
   {
     enum strub_mode mode = get_strub_mode (onode);
@@ -2291,7 +2257,12 @@ pass_ipa_strub::execute (function *)
 		  }
 		gimple_call_set_arg (call, 1, arg);
 		update_stmt (call);
-		e->redirect_callee (cgraph_node::get_create (bvacopy));
+
+		/* If we are causing the cgraph_node for va_copy to be needed,
+		   compute a strub mode for it to make it callable, otherwise
+		   verify_strub would reject it.  */
+		cgraph_node *vacopy_node = cgraph_node::get (bvacopy);
+		e->redirect_callee (vacopy_node);
 	      }
 	    else if (fndecl && apply_args
 		     && fndecl_built_in_p (fndecl, BUILT_IN_APPLY_ARGS))
@@ -2418,7 +2389,14 @@ pass_ipa_strub::execute (function *)
 #endif
   }
 
-  verify_strub ();
+  if (flag_checking)
+    {
+      /* We've already verified before any inlining or other transformations.
+	 Recheck after strub transformations only if checking is enabled, since
+	 they should not introduce any incompatibilities.  */
+      ipa_strub_set_mode_for_new_functions ();
+      verify_strub ();
+    }
 
   return 0;
 }


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [gcc(refs/users/aoliva/heads/strub)] make builtins callable
@ 2021-07-26 20:36 Alexandre Oliva
  0 siblings, 0 replies; 7+ messages in thread
From: Alexandre Oliva @ 2021-07-26 20:36 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:876827d3e0e519d3b90afcdfe5c694b26c0775ea

commit 876827d3e0e519d3b90afcdfe5c694b26c0775ea
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Mon Jul 26 12:15:36 2021 -0300

    make builtins callable

Diff:
---
 gcc/ipa-strub.c | 298 ++++++++++++++++++++++++--------------------------------
 1 file changed, 128 insertions(+), 170 deletions(-)

diff --git a/gcc/ipa-strub.c b/gcc/ipa-strub.c
index 7b05321e980..05c742661d0 100644
--- a/gcc/ipa-strub.c
+++ b/gcc/ipa-strub.c
@@ -438,6 +438,34 @@ strub_from_body_p (cgraph_node *node)
   return false;
 }
 
+/* Return true iff node is associated with a builtin that should be callable
+   from strub contexts.  */
+static inline bool
+strub_callable_builtin_p (cgraph_node *node)
+{
+  if (DECL_BUILT_IN_CLASS (node->decl) != BUILT_IN_NORMAL)
+    return false;
+
+  enum built_in_function fcode = DECL_FUNCTION_CODE (node->decl);
+
+  switch (fcode)
+    {
+    case BUILT_IN_NONE:
+      gcc_unreachable ();
+
+      /* ??? Make all builtins callable.  We wish to make any builtin call the
+	 compiler might introduce on its own callable.  Anything that is
+	 predictable enough as to be known not to allow stack data that should
+	 be strubbed to unintentionally escape to non-strub contexts can be
+	 allowed, and pretty much every builtin appears to fit this description.
+	 The exceptions to this rule seem to be rare, and only available as
+	 explicit __builtin calls, so let's keep it simple and allow all of
+	 them...  */
+    default:
+      return true;
+    }
+}
+
 static enum strub_mode
 compute_strub_mode (cgraph_node *node, tree strub_attr)
 {
@@ -495,7 +523,8 @@ compute_strub_mode (cgraph_node *node, tree strub_attr)
     = (!strub_flag_disabled
        && (strub_attr
 	   ? req_mode == STRUB_CALLABLE
-	   : strub_flag_viable));
+	   : (strub_flag_viable
+	      || strub_callable_builtin_p (node))));
 
   /* This is a shorthand for either strub-enabled mode.  */
   const bool consider_strub
@@ -845,6 +874,49 @@ strub_inlinable_p (cgraph_node *callee, cgraph_node *caller)
   return false;
 }
 
+/* Check that strub functions don't call non-strub functions, and that
+   always_inline strub functions are only called by strub
+   functions.  */
+static void
+verify_strub ()
+{
+  cgraph_node *node;
+
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+  {
+    enum strub_mode caller_mode = get_strub_mode (node);
+    bool strub_context
+      = (caller_mode == STRUB_AT_CALLS
+	 || caller_mode == STRUB_WRAPPED
+	 || caller_mode == STRUB_INLINABLE);
+
+    for (cgraph_edge *e = node->callees; e; e = e->next_callee)
+      if (e->indirect_unknown_callee)
+	{
+	  if (!strub_context)
+	    continue;
+
+	  tree callee_fntype = gimple_call_fntype (e->call_stmt);
+	  enum strub_mode callee_mode
+	    = get_strub_mode_from_type (callee_fntype);
+
+	  if (callee_mode == STRUB_DISABLED
+	      || callee_mode == STRUB_INTERNAL)
+	    error_at (gimple_location (e->call_stmt),
+		      "indirect non-strub call in strub context %qD",
+		      node->decl);
+	}
+      else if (!strub_callable_from_p (e->callee, node))
+	error_at (gimple_location (e->call_stmt),
+		  "calling non-strub %qD in strub context %qD",
+		  e->callee->decl, node->decl);
+  }
+
+  /* ??? Check strub-wise pointer type compatibility of variables and
+     functions, or is this already taken care of on account of the
+     attribute's being marked as affecting type identity?  */
+}
+
 namespace {
 
 const pass_data pass_data_ipa_strub_mode = {
@@ -907,8 +979,6 @@ public:
 	   NULL, NULL);						\
 	TREE_NOTHROW (decl) = true;				\
 	set_builtin_decl ((CODE), decl, true);			\
-	set_strub_mode_to (cgraph_node::get_create (decl),	\
-			   STRUB_CALLABLE);			\
       }								\
     return decl;						\
   }
@@ -939,8 +1009,6 @@ public:
 	   "__strub_" #NAME, attrs);				\
 	TREE_NOTHROW (decl) = true;				\
 	set_builtin_decl ((CODE), decl, true);			\
-	set_strub_mode_to (cgraph_node::get_create (decl),	\
-			   STRUB_CALLABLE);			\
       }								\
     return decl;						\
   }
@@ -1014,116 +1082,6 @@ public:
 
 } // anon namespace
 
-#if 0
-static bool
-may_throw_p (gcall *stmt)
-{
-  return flag_exceptions && !gimple_call_nothrow_p (stmt);
-}
-
-static bool
-strub_this_call_p (gcall *stmt)
-{
-  if (gimple_call_internal_p (stmt))
-    return false;
-
-  /* If there's no outgoing path in which to do the scrubbing, don't
-     bother.  */
-  if (gimple_call_noreturn_p (stmt) && !may_throw_p (stmt))
-    return false;
-
-  /* ??? Maybe non-mandatory tail calls should be disabled for
-     scrubbing.  Or maybe it won't matter, as long as both tail-caller
-     and callee are scrubbing-capable.  */
-  if (gimple_call_must_tail_p (stmt) || gimple_call_tail_p (stmt))
-    return false;
-
-  if (gimple_alloca_call_p (stmt))
-    return true;
-
-  tree fndecl = gimple_call_fndecl (stmt);
-  if (!fndecl)
-    return true;
-
-  if (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
-    return true;
-
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-
-  switch (fcode)
-    {
-    CASE_BUILT_IN_ALLOCA:
-      return true;
-
-    case BUILT_IN_NONE:
-      return true;
-
-    case BUILT_IN___STRUB_ENTER:
-    case BUILT_IN___STRUB_UPDATE:
-    case BUILT_IN___STRUB_LEAVE:
-      return false;
-
-    case BUILT_IN_CLASSIFY_TYPE:
-    case BUILT_IN_CONSTANT_P:
-      return false;
-
-    case BUILT_IN_RETURN_ADDRESS:
-    case BUILT_IN_FRAME_ADDRESS:
-    case BUILT_IN_STACK_ADDRESS:
-    case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
-      return false;
-
-    case BUILT_IN_STACK_SAVE:
-    case BUILT_IN_STACK_RESTORE:
-    case BUILT_IN_ASAN_ALLOCAS_UNPOISON:
-      return false;
-
-    case BUILT_IN_SETJMP_SETUP:
-    case BUILT_IN_SETJMP_RECEIVER:
-    case BUILT_IN_LONGJMP:
-    case BUILT_IN_NONLOCAL_GOTO:
-    case BUILT_IN_UPDATE_SETJMP_BUF:
-    case BUILT_IN_TRAP:
-    case BUILT_IN_UNREACHABLE:
-      return false;
-
-    case BUILT_IN_UNWIND_INIT:
-    case BUILT_IN_DWARF_CFA:
-#ifdef DWARF2_UNWIND_INFO
-    case BUILT_IN_DWARF_SP_COLUMN:
-    case BUILT_IN_INIT_DWARF_REG_SIZES:
-#endif
-    case BUILT_IN_FROB_RETURN_ADDR:
-    case BUILT_IN_EXTRACT_RETURN_ADDR:
-    case BUILT_IN_EH_RETURN:
-    case BUILT_IN_EH_RETURN_DATA_REGNO:
-    case BUILT_IN_EXTEND_POINTER:
-    case BUILT_IN_EH_POINTER:
-    case BUILT_IN_EH_FILTER:
-    case BUILT_IN_EH_COPY_VALUES:
-      return false;
-
-    case BUILT_IN_VA_START:
-    case BUILT_IN_VA_END:
-    case BUILT_IN_VA_COPY:
-    case BUILT_IN_EXPECT:
-    case BUILT_IN_EXPECT_WITH_PROBABILITY:
-    case BUILT_IN_ASSUME_ALIGNED:
-    case BUILT_IN_PREFETCH:
-      return false;
-
-    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
-    case BUILT_IN_OBJECT_SIZE:
-    case BUILT_IN_THREAD_POINTER:
-    case BUILT_IN_SET_THREAD_POINTER:
-      return false;
-
-    default:
-      return true;
-    }
-}
-#endif
-
 typedef hash_set<tree> indirect_parms_t;
 
 static tree
@@ -1402,32 +1360,61 @@ remove_named_attribute_unsharing (const char *name, tree *attrs)
     }
 }
 
-unsigned int
-pass_ipa_strub_mode::execute (function *)
+static int last_cgraph_order;
+
+static bool
+ipa_strub_set_mode_for_new_functions ()
 {
-  cgraph_node *onode;
+  if (last_cgraph_order && symtab->order == last_cgraph_order)
+    /* If we're called again after the first call,
+       then the first call must have returned true.  */
+    return true;
 
-  /* If no strub flag was given in the command line,
-     set the actual default.  */
-  if (flag_strub == -2)
-    flag_strub = 3; // for testing only; was: -1;
+  cgraph_node *node;
 
   bool any_strub = false;
 
+  /* Go through the functions twice, once over non-aliases, and then over
+     aliases, so that aliases can reuse the mode computation of their ultimate
+     targets.  */
   for (int aliases = 0; aliases <= 1; aliases++)
-    FOR_EACH_FUNCTION (onode)
+    FOR_EACH_FUNCTION (node)
     {
-      if (!onode->alias != !aliases)
+      if (!node->alias != !aliases)
 	continue;
 
-      enum strub_mode mode = set_strub_mode (onode);
+      /*  Already done.  */
+      if (node->order < last_cgraph_order)
+	continue;
+
+      enum strub_mode mode = set_strub_mode (node);
 
       if (mode == STRUB_AT_CALLS || mode == STRUB_INTERNAL)
 	any_strub = true;
     }
 
+  if (any_strub)
+    last_cgraph_order = symtab->order;
+
+  return any_strub;
+}
+
+unsigned int
+pass_ipa_strub_mode::execute (function *)
+{
+  /* If no strub flag was given in the command line,
+     set the actual default.  */
+  if (flag_strub == -2)
+    flag_strub = 3; // for testing only; was: -1;
+
+  last_cgraph_order = 0;
+  bool any_strub = ipa_strub_set_mode_for_new_functions ();
+
   if (!any_strub)
     flag_strub = 0;
+  else
+    /* Verify before any inlining or other transformations.  */
+    verify_strub ();
 
   return 0;
 }
@@ -1438,54 +1425,13 @@ make_pass_ipa_strub_mode (gcc::context *ctxt)
   return new pass_ipa_strub_mode (ctxt);
 }
 
-/* Check that strub functions don't call non-strub functions, and that
-   always_inline strub functions are only called by strub
-   functions.  */
-static void
-verify_strub ()
-{
-  cgraph_node *node;
-
-  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
-  {
-    enum strub_mode caller_mode = get_strub_mode (node);
-    bool strub_context
-      = (caller_mode == STRUB_AT_CALLS
-	 || caller_mode == STRUB_WRAPPED
-	 || caller_mode == STRUB_INLINABLE);
-
-    for (cgraph_edge *e = node->callees; e; e = e->next_callee)
-      if (e->indirect_unknown_callee)
-	{
-	  if (!strub_context)
-	    continue;
-
-	  tree callee_fntype = gimple_call_fntype (e->call_stmt);
-	  enum strub_mode callee_mode
-	    = get_strub_mode_from_type (callee_fntype);
-
-	  if (callee_mode == STRUB_DISABLED
-	      || callee_mode == STRUB_INTERNAL)
-	    error_at (gimple_location (e->call_stmt),
-		      "indirect non-strub call in strub context %qD",
-		      node->decl);
-	}
-      else if (!strub_callable_from_p (e->callee, node))
-	error_at (gimple_location (e->call_stmt),
-		  "calling non-strub %qD in strub context %qD",
-		  e->callee->decl, node->decl);
-  }
-
-  /* ??? Check strub-wise pointer type compatibility of variables and
-     functions, or is this already taken care of on account of the
-     attribute's being marked as affecting type identity?  */
-}
-
 unsigned int
 pass_ipa_strub::execute (function *)
 {
   cgraph_node *onode;
 
+  ipa_strub_set_mode_for_new_functions ();
+
   FOR_EACH_FUNCTION (onode)
   {
     enum strub_mode mode = get_strub_mode (onode);
@@ -2291,7 +2237,12 @@ pass_ipa_strub::execute (function *)
 		  }
 		gimple_call_set_arg (call, 1, arg);
 		update_stmt (call);
-		e->redirect_callee (cgraph_node::get_create (bvacopy));
+
+		/* If we are causing the cgraph_node for va_copy to be needed,
+		   compute a strub mode for it to make it callable, otherwise
+		   verify_strub would reject it.  */
+		cgraph_node *vacopy_node = cgraph_node::get (bvacopy);
+		e->redirect_callee (vacopy_node);
 	      }
 	    else if (fndecl && apply_args
 		     && fndecl_built_in_p (fndecl, BUILT_IN_APPLY_ARGS))
@@ -2418,7 +2369,14 @@ pass_ipa_strub::execute (function *)
 #endif
   }
 
-  verify_strub ();
+  if (flag_checking)
+    {
+      /* We've already verified before any inlining or other transformations.
+	 Recheck after strub transformations only if checking is enabled, since
+	 they should not introduce any incompatibilities.  */
+      ipa_strub_set_mode_for_new_functions ();
+      verify_strub ();
+    }
 
   return 0;
 }


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [gcc(refs/users/aoliva/heads/strub)] make builtins callable
@ 2021-07-26 18:25 Alexandre Oliva
  0 siblings, 0 replies; 7+ messages in thread
From: Alexandre Oliva @ 2021-07-26 18:25 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:2d7e1ec7bce83f538346c36afba432eec861b542

commit 2d7e1ec7bce83f538346c36afba432eec861b542
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Mon Jul 26 12:15:36 2021 -0300

    make builtins callable

Diff:
---
 gcc/ipa-strub.c | 266 +++++++++++++++++++++++---------------------------------
 1 file changed, 110 insertions(+), 156 deletions(-)

diff --git a/gcc/ipa-strub.c b/gcc/ipa-strub.c
index 7b05321e980..7ec606a4c74 100644
--- a/gcc/ipa-strub.c
+++ b/gcc/ipa-strub.c
@@ -438,6 +438,34 @@ strub_from_body_p (cgraph_node *node)
   return false;
 }
 
+/* Return true iff node is associated with a builtin that should be callable
+   from strub contexts.  */
+static inline bool
+strub_callable_builtin_p (cgraph_node *node)
+{
+  if (DECL_BUILT_IN_CLASS (node->decl) != BUILT_IN_NORMAL)
+    return false;
+
+  enum built_in_function fcode = DECL_FUNCTION_CODE (node->decl);
+
+  switch (fcode)
+    {
+    case BUILT_IN_NONE:
+      gcc_unreachable ();
+
+      /* ??? Make all builtins callable.  We wish to make any builtin call the
+	 compiler might introduce on its own callable.  Anything that is
+	 predictable enough as to be known not to allow stack data that should
+	 be strubbed to unintentionally escape to non-strub contexts can be
+	 allowed, and pretty much every builtin appears to fit this description.
+	 The exceptions to this rule seem to be rare, and only available as
+	 explicit __builtin calls, so let's keep it simple and allow all of
+	 them...  */
+    default:
+      return true;
+    }
+}
+
 static enum strub_mode
 compute_strub_mode (cgraph_node *node, tree strub_attr)
 {
@@ -495,7 +523,8 @@ compute_strub_mode (cgraph_node *node, tree strub_attr)
     = (!strub_flag_disabled
        && (strub_attr
 	   ? req_mode == STRUB_CALLABLE
-	   : strub_flag_viable));
+	   : (strub_flag_viable
+	      || strub_callable_builtin_p (node))));
 
   /* This is a shorthand for either strub-enabled mode.  */
   const bool consider_strub
@@ -746,6 +775,14 @@ set_strub_mode (cgraph_node *node)
   return mode;
 }
 
+static cgraph_node_hook_list *strub_cgraph_hook_entry = NULL;
+
+static void
+strub_cgraph_insertion_hook (cgraph_node *node, void *)
+{
+  set_strub_mode (node);
+}
+
 /* Non-strub functions shouldn't be called from strub functions,
    except through callable ones.  Always inline strub functions can
    only be called from strub functions.  */
@@ -845,6 +882,49 @@ strub_inlinable_p (cgraph_node *callee, cgraph_node *caller)
   return false;
 }
 
+/* Check that strub functions don't call non-strub functions, and that
+   always_inline strub functions are only called by strub
+   functions.  */
+static void
+verify_strub ()
+{
+  cgraph_node *node;
+
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+  {
+    enum strub_mode caller_mode = get_strub_mode (node);
+    bool strub_context
+      = (caller_mode == STRUB_AT_CALLS
+	 || caller_mode == STRUB_WRAPPED
+	 || caller_mode == STRUB_INLINABLE);
+
+    for (cgraph_edge *e = node->callees; e; e = e->next_callee)
+      if (e->indirect_unknown_callee)
+	{
+	  if (!strub_context)
+	    continue;
+
+	  tree callee_fntype = gimple_call_fntype (e->call_stmt);
+	  enum strub_mode callee_mode
+	    = get_strub_mode_from_type (callee_fntype);
+
+	  if (callee_mode == STRUB_DISABLED
+	      || callee_mode == STRUB_INTERNAL)
+	    error_at (gimple_location (e->call_stmt),
+		      "indirect non-strub call in strub context %qD",
+		      node->decl);
+	}
+      else if (!strub_callable_from_p (e->callee, node))
+	error_at (gimple_location (e->call_stmt),
+		  "calling non-strub %qD in strub context %qD",
+		  e->callee->decl, node->decl);
+  }
+
+  /* ??? Check strub-wise pointer type compatibility of variables and
+     functions, or is this already taken care of on account of the
+     attribute's being marked as affecting type identity?  */
+}
+
 namespace {
 
 const pass_data pass_data_ipa_strub_mode = {
@@ -1014,116 +1094,6 @@ public:
 
 } // anon namespace
 
-#if 0
-static bool
-may_throw_p (gcall *stmt)
-{
-  return flag_exceptions && !gimple_call_nothrow_p (stmt);
-}
-
-static bool
-strub_this_call_p (gcall *stmt)
-{
-  if (gimple_call_internal_p (stmt))
-    return false;
-
-  /* If there's no outgoing path in which to do the scrubbing, don't
-     bother.  */
-  if (gimple_call_noreturn_p (stmt) && !may_throw_p (stmt))
-    return false;
-
-  /* ??? Maybe non-mandatory tail calls should be disabled for
-     scrubbing.  Or maybe it won't matter, as long as both tail-caller
-     and callee are scrubbing-capable.  */
-  if (gimple_call_must_tail_p (stmt) || gimple_call_tail_p (stmt))
-    return false;
-
-  if (gimple_alloca_call_p (stmt))
-    return true;
-
-  tree fndecl = gimple_call_fndecl (stmt);
-  if (!fndecl)
-    return true;
-
-  if (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
-    return true;
-
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-
-  switch (fcode)
-    {
-    CASE_BUILT_IN_ALLOCA:
-      return true;
-
-    case BUILT_IN_NONE:
-      return true;
-
-    case BUILT_IN___STRUB_ENTER:
-    case BUILT_IN___STRUB_UPDATE:
-    case BUILT_IN___STRUB_LEAVE:
-      return false;
-
-    case BUILT_IN_CLASSIFY_TYPE:
-    case BUILT_IN_CONSTANT_P:
-      return false;
-
-    case BUILT_IN_RETURN_ADDRESS:
-    case BUILT_IN_FRAME_ADDRESS:
-    case BUILT_IN_STACK_ADDRESS:
-    case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
-      return false;
-
-    case BUILT_IN_STACK_SAVE:
-    case BUILT_IN_STACK_RESTORE:
-    case BUILT_IN_ASAN_ALLOCAS_UNPOISON:
-      return false;
-
-    case BUILT_IN_SETJMP_SETUP:
-    case BUILT_IN_SETJMP_RECEIVER:
-    case BUILT_IN_LONGJMP:
-    case BUILT_IN_NONLOCAL_GOTO:
-    case BUILT_IN_UPDATE_SETJMP_BUF:
-    case BUILT_IN_TRAP:
-    case BUILT_IN_UNREACHABLE:
-      return false;
-
-    case BUILT_IN_UNWIND_INIT:
-    case BUILT_IN_DWARF_CFA:
-#ifdef DWARF2_UNWIND_INFO
-    case BUILT_IN_DWARF_SP_COLUMN:
-    case BUILT_IN_INIT_DWARF_REG_SIZES:
-#endif
-    case BUILT_IN_FROB_RETURN_ADDR:
-    case BUILT_IN_EXTRACT_RETURN_ADDR:
-    case BUILT_IN_EH_RETURN:
-    case BUILT_IN_EH_RETURN_DATA_REGNO:
-    case BUILT_IN_EXTEND_POINTER:
-    case BUILT_IN_EH_POINTER:
-    case BUILT_IN_EH_FILTER:
-    case BUILT_IN_EH_COPY_VALUES:
-      return false;
-
-    case BUILT_IN_VA_START:
-    case BUILT_IN_VA_END:
-    case BUILT_IN_VA_COPY:
-    case BUILT_IN_EXPECT:
-    case BUILT_IN_EXPECT_WITH_PROBABILITY:
-    case BUILT_IN_ASSUME_ALIGNED:
-    case BUILT_IN_PREFETCH:
-      return false;
-
-    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
-    case BUILT_IN_OBJECT_SIZE:
-    case BUILT_IN_THREAD_POINTER:
-    case BUILT_IN_SET_THREAD_POINTER:
-      return false;
-
-    default:
-      return true;
-    }
-}
-#endif
-
 typedef hash_set<tree> indirect_parms_t;
 
 static tree
@@ -1414,6 +1384,9 @@ pass_ipa_strub_mode::execute (function *)
 
   bool any_strub = false;
 
+  /* Go through the functions twice, once over non-aliases, and then over
+     aliases, so that aliases can reuse the mode computation of their ultimate
+     targets.  */
   for (int aliases = 0; aliases <= 1; aliases++)
     FOR_EACH_FUNCTION (onode)
     {
@@ -1428,6 +1401,14 @@ pass_ipa_strub_mode::execute (function *)
 
   if (!any_strub)
     flag_strub = 0;
+  else
+    {
+      /* Verify before any inlining or other transformations.  */
+      verify_strub ();
+
+      strub_cgraph_hook_entry = symtab->add_cgraph_insertion_hook
+	(strub_cgraph_insertion_hook, NULL);
+    }
 
   return 0;
 }
@@ -1438,49 +1419,6 @@ make_pass_ipa_strub_mode (gcc::context *ctxt)
   return new pass_ipa_strub_mode (ctxt);
 }
 
-/* Check that strub functions don't call non-strub functions, and that
-   always_inline strub functions are only called by strub
-   functions.  */
-static void
-verify_strub ()
-{
-  cgraph_node *node;
-
-  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
-  {
-    enum strub_mode caller_mode = get_strub_mode (node);
-    bool strub_context
-      = (caller_mode == STRUB_AT_CALLS
-	 || caller_mode == STRUB_WRAPPED
-	 || caller_mode == STRUB_INLINABLE);
-
-    for (cgraph_edge *e = node->callees; e; e = e->next_callee)
-      if (e->indirect_unknown_callee)
-	{
-	  if (!strub_context)
-	    continue;
-
-	  tree callee_fntype = gimple_call_fntype (e->call_stmt);
-	  enum strub_mode callee_mode
-	    = get_strub_mode_from_type (callee_fntype);
-
-	  if (callee_mode == STRUB_DISABLED
-	      || callee_mode == STRUB_INTERNAL)
-	    error_at (gimple_location (e->call_stmt),
-		      "indirect non-strub call in strub context %qD",
-		      node->decl);
-	}
-      else if (!strub_callable_from_p (e->callee, node))
-	error_at (gimple_location (e->call_stmt),
-		  "calling non-strub %qD in strub context %qD",
-		  e->callee->decl, node->decl);
-  }
-
-  /* ??? Check strub-wise pointer type compatibility of variables and
-     functions, or is this already taken care of on account of the
-     attribute's being marked as affecting type identity?  */
-}
-
 unsigned int
 pass_ipa_strub::execute (function *)
 {
@@ -2291,7 +2229,17 @@ pass_ipa_strub::execute (function *)
 		  }
 		gimple_call_set_arg (call, 1, arg);
 		update_stmt (call);
-		e->redirect_callee (cgraph_node::get_create (bvacopy));
+
+		/* If we are causing the cgraph_node for va_copy to be needed,
+		   compute a strub mode for it to make it callable, otherwise
+		   verify_strub would reject it.  */
+		cgraph_node *vacopy_node = cgraph_node::get (bvacopy);
+		if (!vacopy_node)
+		  {
+		    vacopy_node = cgraph_node::get_create (bvacopy);
+		    set_strub_mode (vacopy_node);
+		  }
+		e->redirect_callee (vacopy_node);
 	      }
 	    else if (fndecl && apply_args
 		     && fndecl_built_in_p (fndecl, BUILT_IN_APPLY_ARGS))
@@ -2418,7 +2366,13 @@ pass_ipa_strub::execute (function *)
 #endif
   }
 
-  verify_strub ();
+  if (flag_checking)
+    /* We've already verified before any inlining or other transformations.
+       Recheck after strub transformations only if checking is enabled, since
+       they should not introduce any incompatibilities.  */
+    verify_strub ();
+
+  symtab->remove_cgraph_insertion_hook (strub_cgraph_hook_entry);
 
   return 0;
 }


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [gcc(refs/users/aoliva/heads/strub)] make builtins callable
@ 2021-07-26 15:18 Alexandre Oliva
  0 siblings, 0 replies; 7+ messages in thread
From: Alexandre Oliva @ 2021-07-26 15:18 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:55724817dd9d178f5671ec1db4a5d3a836f8928b

commit 55724817dd9d178f5671ec1db4a5d3a836f8928b
Author: Alexandre Oliva <oliva@adacore.com>
Date:   Mon Jul 26 12:15:36 2021 -0300

    make builtins callable

Diff:
---
 gcc/ipa-strub.c | 153 +++++++++++++++-----------------------------------------
 1 file changed, 41 insertions(+), 112 deletions(-)

diff --git a/gcc/ipa-strub.c b/gcc/ipa-strub.c
index 7b05321e980..ba80f2faed9 100644
--- a/gcc/ipa-strub.c
+++ b/gcc/ipa-strub.c
@@ -438,6 +438,34 @@ strub_from_body_p (cgraph_node *node)
   return false;
 }
 
+/* Return true iff node is associated with a builtin that should be callable
+   from strub contexts.  */
+static inline bool
+strub_callable_builtin_p (cgraph_node *node)
+{
+  if (DECL_BUILT_IN_CLASS (node->decl) != BUILT_IN_NORMAL)
+    return false;
+
+  enum built_in_function fcode = DECL_FUNCTION_CODE (node->decl);
+
+  switch (fcode)
+    {
+    case BUILT_IN_NONE:
+      gcc_unreachable ();
+
+      /* ??? Make all builtins callable.  We wish to make any builtin call the
+	 compiler might introduce on its own callable.  Anything that is
+	 predictable enough as to be known not to allow stack data that should
+	 be strubbed to unintentionally escape to non-strub contexts can be
+	 allowed, and pretty much every builtin appears to fit this description.
+	 The exceptions to this rule seem to be rare, and only available as
+	 explicit __builtin calls, so let's keep it simple and allow all of
+	 them...  */
+    default:
+      return true;
+    }
+}
+
 static enum strub_mode
 compute_strub_mode (cgraph_node *node, tree strub_attr)
 {
@@ -495,7 +523,8 @@ compute_strub_mode (cgraph_node *node, tree strub_attr)
     = (!strub_flag_disabled
        && (strub_attr
 	   ? req_mode == STRUB_CALLABLE
-	   : strub_flag_viable));
+	   : (strub_flag_viable
+	      || strub_callable_builtin_p (node))));
 
   /* This is a shorthand for either strub-enabled mode.  */
   const bool consider_strub
@@ -1014,116 +1043,6 @@ public:
 
 } // anon namespace
 
-#if 0
-static bool
-may_throw_p (gcall *stmt)
-{
-  return flag_exceptions && !gimple_call_nothrow_p (stmt);
-}
-
-static bool
-strub_this_call_p (gcall *stmt)
-{
-  if (gimple_call_internal_p (stmt))
-    return false;
-
-  /* If there's no outgoing path in which to do the scrubbing, don't
-     bother.  */
-  if (gimple_call_noreturn_p (stmt) && !may_throw_p (stmt))
-    return false;
-
-  /* ??? Maybe non-mandatory tail calls should be disabled for
-     scrubbing.  Or maybe it won't matter, as long as both tail-caller
-     and callee are scrubbing-capable.  */
-  if (gimple_call_must_tail_p (stmt) || gimple_call_tail_p (stmt))
-    return false;
-
-  if (gimple_alloca_call_p (stmt))
-    return true;
-
-  tree fndecl = gimple_call_fndecl (stmt);
-  if (!fndecl)
-    return true;
-
-  if (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
-    return true;
-
-  enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl);
-
-  switch (fcode)
-    {
-    CASE_BUILT_IN_ALLOCA:
-      return true;
-
-    case BUILT_IN_NONE:
-      return true;
-
-    case BUILT_IN___STRUB_ENTER:
-    case BUILT_IN___STRUB_UPDATE:
-    case BUILT_IN___STRUB_LEAVE:
-      return false;
-
-    case BUILT_IN_CLASSIFY_TYPE:
-    case BUILT_IN_CONSTANT_P:
-      return false;
-
-    case BUILT_IN_RETURN_ADDRESS:
-    case BUILT_IN_FRAME_ADDRESS:
-    case BUILT_IN_STACK_ADDRESS:
-    case BUILT_IN_AGGREGATE_INCOMING_ADDRESS:
-      return false;
-
-    case BUILT_IN_STACK_SAVE:
-    case BUILT_IN_STACK_RESTORE:
-    case BUILT_IN_ASAN_ALLOCAS_UNPOISON:
-      return false;
-
-    case BUILT_IN_SETJMP_SETUP:
-    case BUILT_IN_SETJMP_RECEIVER:
-    case BUILT_IN_LONGJMP:
-    case BUILT_IN_NONLOCAL_GOTO:
-    case BUILT_IN_UPDATE_SETJMP_BUF:
-    case BUILT_IN_TRAP:
-    case BUILT_IN_UNREACHABLE:
-      return false;
-
-    case BUILT_IN_UNWIND_INIT:
-    case BUILT_IN_DWARF_CFA:
-#ifdef DWARF2_UNWIND_INFO
-    case BUILT_IN_DWARF_SP_COLUMN:
-    case BUILT_IN_INIT_DWARF_REG_SIZES:
-#endif
-    case BUILT_IN_FROB_RETURN_ADDR:
-    case BUILT_IN_EXTRACT_RETURN_ADDR:
-    case BUILT_IN_EH_RETURN:
-    case BUILT_IN_EH_RETURN_DATA_REGNO:
-    case BUILT_IN_EXTEND_POINTER:
-    case BUILT_IN_EH_POINTER:
-    case BUILT_IN_EH_FILTER:
-    case BUILT_IN_EH_COPY_VALUES:
-      return false;
-
-    case BUILT_IN_VA_START:
-    case BUILT_IN_VA_END:
-    case BUILT_IN_VA_COPY:
-    case BUILT_IN_EXPECT:
-    case BUILT_IN_EXPECT_WITH_PROBABILITY:
-    case BUILT_IN_ASSUME_ALIGNED:
-    case BUILT_IN_PREFETCH:
-      return false;
-
-    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
-    case BUILT_IN_OBJECT_SIZE:
-    case BUILT_IN_THREAD_POINTER:
-    case BUILT_IN_SET_THREAD_POINTER:
-      return false;
-
-    default:
-      return true;
-    }
-}
-#endif
-
 typedef hash_set<tree> indirect_parms_t;
 
 static tree
@@ -2291,7 +2210,17 @@ pass_ipa_strub::execute (function *)
 		  }
 		gimple_call_set_arg (call, 1, arg);
 		update_stmt (call);
-		e->redirect_callee (cgraph_node::get_create (bvacopy));
+
+		/* If we are causing the cgraph_node for va_copy to be needed,
+		   compute a strub mode for it to make it callable, otherwise
+		   verify_strub will reject it.  */
+		cgraph_node *vacopy_node = cgraph_node::get (bvacopy);
+		if (!vacopy_node)
+		  {
+		    vacopy_node = cgraph_node::get_create (bvacopy);
+		    set_strub_mode (vacopy_node);
+		  }
+		e->redirect_callee (vacopy_node);
 	      }
 	    else if (fndecl && apply_args
 		     && fndecl_built_in_p (fndecl, BUILT_IN_APPLY_ARGS))


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-07-28  4:30 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-26 15:43 [gcc(refs/users/aoliva/heads/strub)] make builtins callable Alexandre Oliva
  -- strict thread matches above, loose matches on Subject: below --
2021-07-28  4:30 Alexandre Oliva
2021-07-26 21:05 Alexandre Oliva
2021-07-26 20:55 Alexandre Oliva
2021-07-26 20:36 Alexandre Oliva
2021-07-26 18:25 Alexandre Oliva
2021-07-26 15:18 Alexandre Oliva

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).