Hi,

the attached Ada testcase compiled with -O -flto exhibits a wrong code issue 
when the 3 optimizations NRV + RSO + inlining are applied to the same call: if 
the LHS of the call is marked write-only before inlining, then it will keep 
the mark after inlining although it may be read in GIMPLE from that point on.

The proposed fix is to apply the removal of the store that would have been 
applied by execute_fixup_cfg if the call was not inlined:

          /* For calls we can simply remove LHS when it is known
             to be write-only.  */
          if (is_gimple_call (stmt)
              && gimple_get_lhs (stmt))
            {
              tree lhs = get_base_address (gimple_get_lhs (stmt));

              if (VAR_P (lhs)
                  && (TREE_STATIC (lhs) || DECL_EXTERNAL (lhs))
                  && varpool_node::get (lhs)->writeonly)
                {
                  gimple_call_set_lhs (stmt, NULL);
                  update_stmt (stmt);
                  todo |= TODO_update_ssa | TODO_cleanup_cfg;
                }
            }

right before inlining, which will prevent the problematic references to the 
LHS from being generated during inlining.

Tested on x86-64/Linux, OK for the mainline?


2024-10-01  Eric Botcazou  <ebotca...@adacore.com>

        * tree-inline.cc (expand_call_inline): Remove the store to the
        return slot if it is a global variable that is only written to.


2024-10-01  Eric Botcazou  <ebotca...@adacore.com>

        * gnat.dg/lto28.adb: New test.
        * gnat.dg/lto28_pkg1.ads: New helper.
        * gnat.dg/lto28_pkg2.ads: Likewise.
        * gnat.dg/lto28_pkg2.adb: Likewise.
        * gnat.dg/lto28_pkg3.ads: Likewise.

-- 
Eric Botcazou
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index f31a34ac410..8d43b033319 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -5130,9 +5130,23 @@ expand_call_inline (basic_block bb, gimple *stmt, copy_body_data *id,
       if (DECL_P (modify_dest))
 	suppress_warning (modify_dest, OPT_Wuninitialized);
 
+      /* If we have a return slot, we can assign it the result directly,
+	 except in the case where it is a global variable that is only
+	 written to because, the callee being permitted to read or take
+	 the address of its DECL_RESULT, this would invalidate the flag
+	 on the global variable; instead we preventively remove the store,
+	 which would have happened later if the call was not inlined.  */
       if (gimple_call_return_slot_opt_p (call_stmt))
 	{
-	  return_slot = modify_dest;
+	  tree base = get_base_address (modify_dest);
+
+	  if (VAR_P (base)
+	      && (TREE_STATIC (base) || DECL_EXTERNAL (base))
+	      && varpool_node::get (base)->writeonly)
+	    return_slot = NULL;
+	  else
+	    return_slot = modify_dest;
+
 	  modify_dest = NULL;
 	}
     }
-- { dg-do run }
-- { dg-options "-O -flto" { target lto } }

with Lto28_Pkg1;

procedure Lto28 is
begin
   null;
end;
with Lto28_Pkg2;

package Lto28_Pkg1 is
   package I is new Lto28_Pkg2.G;
end Lto28_Pkg1;
package body Lto28_Pkg2 is

   function F return Lto28_Pkg3.Q_Rec is
   begin
      return Result : Lto28_Pkg3.Q_Rec := Lto28_Pkg3.Default_Q_Rec do
         Result.A := 1.0;
      end return;
   end;

end Lto28_Pkg2;
package Lto28_Pkg3 is

   type Discr_Type is (P, Q);

   type Rec (Discr : Discr_Type) is record
      case Discr is
         when Q =>
            A : Duration := 0.0;
            B : Duration := 0.0;
         when P =>
            null;
      end case;
   end record;

   subtype Q_Rec is Rec (Q);

   Default_Q_Rec : constant Q_Rec := (Discr => Q, others => <>);

end Lto28_Pkg3;
with Lto28_Pkg3;

package Lto28_Pkg2 is

   function F return Lto28_Pkg3.Q_Rec;

   generic
      Q_Conf : Lto28_Pkg3.Q_Rec := F;
   package G is end;

end Lto28_Pkg2;

Reply via email to