https://gcc.gnu.org/g:addf02282026cc23d24fc4445b47b408d484d1e7

commit r15-5362-gaddf02282026cc23d24fc4445b47b408d484d1e7
Author: Jan Hubicka <hubi...@ucw.cz>
Date:   Sun Nov 17 11:54:10 2024 +0100

    ipa-modref bits for unsequenced and reproducible
    
    C attributes reproducible and unsequenced implies that calling function 
twice
    leads to same effect if parameters are otherwise unchanged (function call
    itself does not count).  This is bit bit stronger that modref's notion of
    nondeterminism that says that same inputs will yield same outputs (function
    call itself does count).
    
    This patch makes reproducible/unsequenced imply determinism and cleans up
    determinism handling.  By itself it is not useful, since we can not make 
use of it
    unless we know what are the inputs/outputs of the function which I plan to 
handle
    by the "fn spec" attribute.
    
    gcc/ChangeLog:
    
            * ipa-modref.cc (modref_summary::useful_p): const/pure implies
            determinism.
            (modref_summary_lto::useful_p): Likewise.
            (ignore_nondeterminism_p): Add CALLEE_FNTYPE parameter; check for
            reproducible/unsequenced
            (modref_access_analysis::record_access_p): Use 
ignore_nondeterminism_p
            when handling volatile accesses.
            (modref_access_analysis::get_access_for_fnspec): Update.
            (modref_access_analysis::process_fnspec): Cleanup handling of 
NOVOPS.
            (modref_access_analysis::analyze_call): Use ignore_nondeterminism_p
            when handling asm statements.
            (modref_access_analysis::analyze_stmt): Update.
            (propagate_unknown_call): Update.
            (modref_propagate_in_scc): Update.
            (ipa_merge_modref_summary_after_inlining): Update.

Diff:
---
 gcc/ipa-modref.cc | 90 ++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 53 insertions(+), 37 deletions(-)

diff --git a/gcc/ipa-modref.cc b/gcc/ipa-modref.cc
index 12ac0e7865a7..08a7740de943 100644
--- a/gcc/ipa-modref.cc
+++ b/gcc/ipa-modref.cc
@@ -336,15 +336,13 @@ modref_summary::useful_p (int ecf_flags, bool check_flags)
       && remove_useless_eaf_flags (static_chain_flags, ecf_flags, false))
     return true;
   if (ecf_flags & ECF_CONST)
-    return ((!side_effects || !nondeterministic)
-           && (ecf_flags & ECF_LOOPING_CONST_OR_PURE));
+    return (!side_effects && (ecf_flags & ECF_LOOPING_CONST_OR_PURE));
   if (loads && !loads->every_base)
     return true;
   else
     kills.release ();
   if (ecf_flags & ECF_PURE)
-    return ((!side_effects || !nondeterministic)
-           && (ecf_flags & ECF_LOOPING_CONST_OR_PURE));
+    return (!side_effects && (ecf_flags & ECF_LOOPING_CONST_OR_PURE));
   return stores && !stores->every_base;
 }
 
@@ -409,15 +407,13 @@ modref_summary_lto::useful_p (int ecf_flags, bool 
check_flags)
       && remove_useless_eaf_flags (static_chain_flags, ecf_flags, false))
     return true;
   if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
-    return ((!side_effects || !nondeterministic)
-           && (ecf_flags & ECF_LOOPING_CONST_OR_PURE));
+    return (!side_effects && (ecf_flags & ECF_LOOPING_CONST_OR_PURE));
   if (loads && !loads->every_base)
     return true;
   else
     kills.release ();
   if (ecf_flags & ECF_PURE)
-    return ((!side_effects || !nondeterministic)
-           && (ecf_flags & ECF_LOOPING_CONST_OR_PURE));
+    return (!side_effects && (ecf_flags & ECF_LOOPING_CONST_OR_PURE));
   return stores && !stores->every_base;
 }
 
@@ -794,13 +790,25 @@ namespace {
 /* Return true if ECF flags says that nondeterminism can be ignored.  */
 
 static bool
-ignore_nondeterminism_p (tree caller, int flags)
+ignore_nondeterminism_p (tree caller, int flags, tree callee_fntype)
 {
-  if (flags & (ECF_CONST | ECF_PURE))
+  int caller_flags = flags_from_decl_or_type (caller);
+  if ((flags | caller_flags) & (ECF_CONST | ECF_PURE))
     return true;
   if ((flags & (ECF_NORETURN | ECF_NOTHROW)) == (ECF_NORETURN | ECF_NOTHROW)
       || (!opt_for_fn (caller, flag_exceptions) && (flags & ECF_NORETURN)))
     return true;
+  /* C language defines unsequenced and reproducible functions
+     to be deterministic.  */
+  if (lookup_attribute ("unsequenced", TYPE_ATTRIBUTES (TREE_TYPE (caller)))
+      || lookup_attribute ("reproducible",
+                          TYPE_ATTRIBUTES (TREE_TYPE (caller))))
+    return true;
+  if (callee_fntype
+      && (lookup_attribute ("unsequenced", TYPE_ATTRIBUTES (callee_fntype))
+         || lookup_attribute ("reproducible",
+                              TYPE_ATTRIBUTES (callee_fntype))))
+    return true;
   return false;
 }
 
@@ -1151,7 +1159,8 @@ modref_access_analysis::record_access_lto 
(modref_records_lto *tt, ao_ref *ref,
 bool
 modref_access_analysis::record_access_p (tree expr)
 {
-  if (TREE_THIS_VOLATILE (expr))
+  if (TREE_THIS_VOLATILE (expr)
+      && !ignore_nondeterminism_p (current_function_decl, 0, NULL))
     {
       if (dump_file)
        fprintf (dump_file, " (volatile; marking nondeterministic) ");
@@ -1288,7 +1297,8 @@ modref_access_analysis::merge_call_side_effects
          changed = true;
        }
       if (!m_summary->nondeterministic && callee_summary->nondeterministic
-         && !ignore_nondeterminism_p (current_function_decl, flags))
+         && !ignore_nondeterminism_p (current_function_decl, flags,
+                                      gimple_call_fntype (call)))
        {
          if (dump_file)
            fprintf (dump_file, " - merging nondeterministic.\n");
@@ -1455,6 +1465,7 @@ modref_access_analysis::get_access_for_fnspec (gcall 
*call, attr_fnspec &fnspec,
     }
   return a;
 }
+
 /* Apply side effects of call STMT to CUR_SUMMARY using FNSPEC.
    If IGNORE_STORES is true ignore them.
    Return false if no useful summary can be produced.   */
@@ -1472,7 +1483,8 @@ modref_access_analysis::process_fnspec (gcall *call)
          && stmt_could_throw_p (cfun, call)))
     {
       set_side_effects ();
-      if (!ignore_nondeterminism_p (current_function_decl, flags))
+      if (!ignore_nondeterminism_p (current_function_decl, flags,
+                                   gimple_call_fntype (call)))
        set_nondeterministic ();
     }
 
@@ -1625,13 +1637,7 @@ modref_access_analysis::analyze_call (gcall *stmt)
       if (dump_file)
        fprintf (dump_file, gimple_call_internal_p (stmt)
                 ? " - Internal call" : " - Indirect call.\n");
-      if (flags & ECF_NOVOPS)
-        {
-         set_side_effects ();
-         set_nondeterministic ();
-        }
-      else
-       process_fnspec (stmt);
+      process_fnspec (stmt);
       return;
     }
   /* We only need to handle internal calls in IPA mode.  */
@@ -1800,7 +1806,8 @@ modref_access_analysis::analyze_stmt (gimple *stmt, bool 
always_executed)
   switch (gimple_code (stmt))
    {
    case GIMPLE_ASM:
-      if (gimple_asm_volatile_p (as_a <gasm *> (stmt)))
+      if (gimple_asm_volatile_p (as_a <gasm *> (stmt))
+         && !ignore_nondeterminism_p (current_function_decl, 0, NULL))
        set_nondeterministic ();
       if (cfun->can_throw_non_call_exceptions
          && stmt_could_throw_p (cfun, stmt))
@@ -4592,17 +4599,20 @@ propagate_unknown_call (cgraph_node *node,
          cur_summary_lto->side_effects = true;
          changed = true;
        }
-      if (cur_summary && !cur_summary->nondeterministic
-         && !ignore_nondeterminism_p (node->decl, ecf_flags))
-       {
-         cur_summary->nondeterministic = true;
-         changed = true;
-       }
-      if (cur_summary_lto && !cur_summary_lto->nondeterministic
-         && !ignore_nondeterminism_p (node->decl, ecf_flags))
+      if (!ignore_nondeterminism_p (node->decl, ecf_flags,
+                                   e->callee ? TREE_TYPE (e->callee->decl)
+                                             : NULL_TREE))
        {
-         cur_summary_lto->nondeterministic = true;
-         changed = true;
+         if (cur_summary && !cur_summary->nondeterministic)
+           {
+             cur_summary->nondeterministic = true;
+             changed = true;
+           }
+         if (cur_summary_lto && !cur_summary_lto->nondeterministic)
+           {
+             cur_summary_lto->nondeterministic = true;
+             changed = true;
+           }
        }
     }
   if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
@@ -4869,14 +4879,18 @@ modref_propagate_in_scc (cgraph_node *component_node)
                }
              if (callee_summary && !cur_summary->nondeterministic
                  && callee_summary->nondeterministic
-                 && !ignore_nondeterminism_p (cur->decl, flags))
+                 && !ignore_nondeterminism_p
+                         (cur->decl, flags,
+                          TREE_TYPE (callee_edge->callee->decl)))
                {
                  cur_summary->nondeterministic = true;
                  changed = true;
                }
              if (callee_summary_lto && !cur_summary_lto->nondeterministic
                  && callee_summary_lto->nondeterministic
-                 && !ignore_nondeterminism_p (cur->decl, flags))
+                 && !ignore_nondeterminism_p
+                         (cur->decl, flags,
+                          TREE_TYPE (callee_edge->callee->decl)))
                {
                  cur_summary_lto->nondeterministic = true;
                  changed = true;
@@ -5358,20 +5372,22 @@ ipa_merge_modref_summary_after_inlining (cgraph_edge 
*edge)
   if (!(flags & (ECF_CONST | ECF_PURE))
       || (flags & ECF_LOOPING_CONST_OR_PURE))
     {
+      bool set_nondeterministic
+             = !ignore_nondeterminism_p
+                     (edge->caller->decl, flags,
+                      TREE_TYPE (edge->callee->decl));
       if (to_info)
        {
          if (!callee_info || callee_info->side_effects)
            to_info->side_effects = true;
-         if ((!callee_info || callee_info->nondeterministic)
-             && !ignore_nondeterminism_p (edge->caller->decl, flags))
+         if (set_nondeterministic)
            to_info->nondeterministic = true;
        }
       if (to_info_lto)
        {
          if (!callee_info_lto || callee_info_lto->side_effects)
            to_info_lto->side_effects = true;
-         if ((!callee_info_lto || callee_info_lto->nondeterministic)
-             && !ignore_nondeterminism_p (edge->caller->decl, flags))
+         if (set_nondeterministic)
            to_info_lto->nondeterministic = true;
        }
      }

Reply via email to