From 93f8cb4965cebee125f96376367f05e18ee5749b Mon Sep 17 00:00:00 2001
From: Eric Botcazou <ebotcazou@adacore.com>
Date: Tue, 4 May 2021 12:47:11 +0200
Subject: [PATCH] Reuse non-gimple_reg variable for inlining

When a call to a function is inlined and takes a parameter whose type is not
gimple_reg, a local variable is created in the caller to hold a copy of the
argument passed in the call with the following comment:

      /* We may produce non-gimple trees by adding NOPs or introduce
         invalid sharing when operand is not really constant.
         It is not big deal to prohibit constant propagation here as
         we will constant propagate in DOM1 pass anyway.  *

Of course the second sentence of the comment does not apply to non-gimple_reg
values, unless they get SRAed later, because we don't do constant propagation
for them.  This for example prevents two identical calls to a pure function
from being merged in the attached Ada testcase.

Therefore the attached patch attempts to reuse a read-only or non-addressable
local DECL of the caller, the hitch being that expand_call_inline needs to be
prevented from creating a CLOBBER for the case where it ends uo being reused.

gcc/
	* tree-inline.c (insert_debug_decl_map): Delete.
	(copy_debug_stmt): Minor tweak.
	(setup_one_parameter): Do not use a variable if the value is either
	a read-only DECL or a non-addressable local variable in the caller.
	In this case, insert the debug-only variable in the map manually.
	(expand_call_inline): Do not generate a CLOBBER for these values.
	* tree-inline.h (debug_map): Minor tweak.
---
 gcc/testsuite/gnat.dg/opt94.adb     | 17 +++++++++
 gcc/testsuite/gnat.dg/opt94_pkg.adb | 31 ++++++++++++++++
 gcc/testsuite/gnat.dg/opt94_pkg.ads | 11 ++++++
 gcc/tree-inline.c                   | 56 +++++++++++------------------
 gcc/tree-inline.h                   |  6 ++--
 5 files changed, 83 insertions(+), 38 deletions(-)
 create mode 100644 gcc/testsuite/gnat.dg/opt94.adb
 create mode 100644 gcc/testsuite/gnat.dg/opt94_pkg.adb
 create mode 100644 gcc/testsuite/gnat.dg/opt94_pkg.ads

diff --git a/gcc/testsuite/gnat.dg/opt94.adb b/gcc/testsuite/gnat.dg/opt94.adb
new file mode 100644
index 000000000000..547bef3a9185
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/opt94.adb
@@ -0,0 +1,17 @@
+-- { dg-do compile }
+-- { dg-options "-O -gnatn -fdump-tree-optimized" }
+
+with Opt94_Pkg; use Opt94_Pkg;
+
+function Opt94 (S : String) return Integer is
+  A : constant String := Get;
+
+begin
+  if Valid_Result (A) then
+    return Result (A);
+  else
+    return -1;
+  end if;
+end;
+
+-- { dg-final { scan-tree-dump-times "worker" 1 "optimized" } }
diff --git a/gcc/testsuite/gnat.dg/opt94_pkg.adb b/gcc/testsuite/gnat.dg/opt94_pkg.adb
new file mode 100644
index 000000000000..670291712fb8
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/opt94_pkg.adb
@@ -0,0 +1,31 @@
+package body Opt94_Pkg is
+
+  function Worker (S : String) return Integer;
+  pragma Pure_Function (Worker);
+
+  function Valid_Result (S : String) return Boolean is
+  begin
+    return Worker (S) > 0;
+  end;
+
+  function Result (S : String) return Integer is
+    R : constant Integer := Worker (S);
+  begin
+    if R > 0 then
+      return R;
+    else
+      raise Program_Error;
+    end if;
+  end;
+
+  function Worker (S : String) return Integer is
+  begin
+    return Character'Pos (S (S'First));
+  end;
+
+  function Get return String is
+  begin
+    return "";
+  end;
+
+end Opt94_Pkg;
diff --git a/gcc/testsuite/gnat.dg/opt94_pkg.ads b/gcc/testsuite/gnat.dg/opt94_pkg.ads
new file mode 100644
index 000000000000..16e34338d552
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/opt94_pkg.ads
@@ -0,0 +1,11 @@
+package Opt94_Pkg is
+
+  function Valid_Result (S : String) return Boolean;
+  pragma Inline (Valid_Result);
+
+  function Result (S : String) return Integer;
+  pragma Inline (Result);
+
+  function Get return String;
+
+end Opt94_Pkg;
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 1dcb31c02676..49a5850f4104 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -152,30 +152,6 @@ insert_decl_map (copy_body_data *id, tree key, tree value)
     id->decl_map->put (value, value);
 }
 
-/* Insert a tree->tree mapping for ID.  This is only used for
-   variables.  */
-
-static void
-insert_debug_decl_map (copy_body_data *id, tree key, tree value)
-{
-  if (!gimple_in_ssa_p (id->src_cfun))
-    return;
-
-  if (!opt_for_fn (id->dst_fn, flag_var_tracking_assignments))
-    return;
-
-  if (!target_for_debug_bind (key))
-    return;
-
-  gcc_assert (TREE_CODE (key) == PARM_DECL);
-  gcc_assert (VAR_P (value));
-
-  if (!id->debug_map)
-    id->debug_map = new hash_map<tree, tree>;
-
-  id->debug_map->put (key, value);
-}
-
 /* If nonzero, we're remapping the contents of inlined debug
    statements.  If negative, an error has occurred, such as a
    reference to a variable that isn't available in the inlined
@@ -3190,7 +3166,8 @@ copy_debug_stmt (gdebug *stmt, copy_body_data *id)
   else
     gcc_unreachable ();
 
-  if (TREE_CODE (t) == PARM_DECL && id->debug_map
+  if (TREE_CODE (t) == PARM_DECL
+      && id->debug_map
       && (n = id->debug_map->get (t)))
     {
       gcc_assert (VAR_P (*n));
@@ -3460,16 +3437,18 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn,
      value.  */
   if (TREE_READONLY (p)
       && !TREE_ADDRESSABLE (p)
-      && value && !TREE_SIDE_EFFECTS (value)
+      && value
+      && !TREE_SIDE_EFFECTS (value)
       && !def)
     {
-      /* We may produce non-gimple trees by adding NOPs or introduce
-	 invalid sharing when operand is not really constant.
-	 It is not big deal to prohibit constant propagation here as
-	 we will constant propagate in DOM1 pass anyway.  */
-      if (is_gimple_min_invariant (value)
-	  && useless_type_conversion_p (TREE_TYPE (p),
-						 TREE_TYPE (value))
+      /* We may produce non-gimple trees by adding NOPs or introduce invalid
+	 sharing when the value is not constant or DECL.  And we need to make
+	 sure that it cannot be modified from another path in the callee.  */
+      if ((is_gimple_min_invariant (value)
+	   || (DECL_P (value) && TREE_READONLY (value))
+	   || (auto_var_in_fn_p (value, id->src_fn)
+	       && !TREE_ADDRESSABLE (value)))
+	  && useless_type_conversion_p (TREE_TYPE (p), TREE_TYPE (value))
 	  /* We have to be very careful about ADDR_EXPR.  Make sure
 	     the base variable isn't a local variable of the inlined
 	     function, e.g., when doing recursive inlining, direct or
@@ -3478,7 +3457,9 @@ setup_one_parameter (copy_body_data *id, tree p, tree value, tree fn,
 	  && ! self_inlining_addr_expr (value, fn))
 	{
 	  insert_decl_map (id, p, value);
-	  insert_debug_decl_map (id, p, var);
+	  if (!id->debug_map)
+	    id->debug_map = new hash_map<tree, tree>;
+	  id->debug_map->put (p, var);
 	  return insert_init_debug_bind (id, bb, var, value, NULL);
 	}
     }
@@ -5128,8 +5109,13 @@ expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id,
     for (tree p = DECL_ARGUMENTS (id->src_fn); p; p = DECL_CHAIN (p))
       if (!TREE_THIS_VOLATILE (p))
 	{
+	  /* The value associated with P is a local temporary only if
+	     there is no value associated with P in the debug map.  */
 	  tree *varp = id->decl_map->get (p);
-	  if (varp && VAR_P (*varp) && !is_gimple_reg (*varp))
+	  if (varp
+	      && VAR_P (*varp)
+	      && !is_gimple_reg (*varp)
+	      && !(id->debug_map && id->debug_map->get (p)))
 	    {
 	      tree clobber = build_clobber (TREE_TYPE (*varp));
 	      gimple *clobber_stmt;
diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h
index f20cff31f095..ec0e82fe865e 100644
--- a/gcc/tree-inline.h
+++ b/gcc/tree-inline.h
@@ -150,9 +150,9 @@ struct copy_body_data
   vec<gdebug *> debug_stmts;
 
   /* A map from local declarations in the inlined function to
-     equivalents in the function into which it is being inlined, where
-     the originals have been mapped to a value rather than to a
-     variable.  */
+     equivalents in the function into which it is being inlined,
+     where the originals have been mapped to a value rather than
+     to a variable.  */
   hash_map<tree, tree> *debug_map;
 
   /* A map from the inlined functions dependence info cliques to
-- 
GitLab