On Fri, 22 Nov 2024, Jakub Jelinek wrote:

> On Tue, Nov 19, 2024 at 01:52:06PM +0100, Jan Hubicka wrote:
> > > On Tue, Nov 19, 2024 at 11:23:31AM +0100, Jakub Jelinek wrote:
> > > > On Tue, Nov 19, 2024 at 10:25:16AM +0100, Richard Biener wrote:
> > > > > I think it's pretty clear and easy to describe to users what "m " and 
> > > > > what "mC" do.  But with "pure" this is an odd intermediate state.  
> > > > > For both
> > > > > "m " and "mP" you suggest above the new/delete might modify their
> > > > > global state but as you can't rely on the new/delete pair to prevail
> > > > > you cannot rely on the modification to happen.  But how do you explain
> > > > > that
> > > > 
> > > > If we are willing to make the default not strictly conforming (i.e.
> > > > basically revert PR101480 by default and make the GCC 11.1/11.2 behavior
> > > > the default and allow -fno-sane-operators-new-delete to change to GCC
> > > > 11.3/14.* behavior), I can live with it.
> > > > But we need to make the documentation clear that the default is not 
> > > > strictly
> > > > conforming.
> > > 
> > > Here is a modified version of the patch to do that.
> > > 
> > > Or do we want to set the default based on -std= option (-std=gnu* implies
> > > -fassume-sane-operators-new-delete, -std=c++* implies
> > > -fno-assume-sane-operators-new-delete)?  Though, not sure what to do for
> > > LTO then.
> > 
> > My oriignal plan was to add " sane" attribute to the declarations and
> > prevent them from being merged.  Then every direct call to new/delete
> > would know if it came from sane or insane translation unit.
> > 
> > Alternatively one can also declare
> >  +C++ ObjC++ LTO Var(flag_assume_sane_operators_new_delete) Init(1)
> >  +Assume C++ replaceable global operators new, new[], delete, delete[] 
> > don't read or write visible global state.
> > as optimization.  Then sanity would be function specific.
> > 
> > inline_call contains code that drops flag_strict_aliasing for function
> > when it inlines -fno-strict-alising function into -fstrict-aliasing.
> > At same place we can make new/delete operator insanity similarly
> > contagious.  If you inline function that has insane new/delete calls you
> > make the combined function also insane.
> 
> Here is an updated patch, which makes it Optimize and merges it during
> inlining (pessimistically).
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

OK.

> Note, perhaps it could be (maybe incrementally) refined to only clear
> the flag on inlining if the callee actually has any
> gimple_call_from_new_or_delete stmts calling DECL_IS_REPLACEABLE_OPERATOR.
> I.e. like it collects whether a function uses floating point operations
> also gather this in another flag.

After 64bit location_t we have spare flags in gimple, I think we could
reserve a flag to indicate something with that something dependent on
the function called - like in the gimple_call_from_new_or_delete case
whether it's considered "sane" or not.  This would solve the inlining
case (we now also have two bits left in GF_CALL, so possible even now)

Thanks,
Richard.

> 2024-11-22  Jakub Jelinek  <ja...@redhat.com>
> 
>       PR c++/110137
>       PR middle-end/101480
> gcc/
>       * doc/invoke.texi (-fassume-sane-operators-new-delete,
>       -fno-assume-sane-operators-new-delete): Document.
>       * gimple.cc (gimple_call_fnspec): Handle
>       -f{,no-}assume-sane-operators-new-delete.
>       * ipa-inline-transform.cc (inline_call): Also clear
>       flag_assume_sane_operators_new_delete on caller when inlining
>       -fno-assume-sane-operators-new-delete callee into
>       -fassume-sane-operators-new-delete caller.
> gcc/c-family/
>       * c.opt (fassume-sane-operators-new-delete): New option.
> gcc/testsuite/
>       * g++.dg/tree-ssa/pr110137-1.C: New test.
>       * g++.dg/tree-ssa/pr110137-2.C: New test.
>       * g++.dg/tree-ssa/pr110137-3.C: New test.
>       * g++.dg/tree-ssa/pr110137-4.C: New test.
>       * g++.dg/torture/pr10148.C: Add -fno-assume-sane-operators-new-delete
>       as dg-additional-options.
>       * g++.dg/warn/Warray-bounds-16.C: Revert 2021-11-10 changes.
> 
> --- gcc/doc/invoke.texi.jj    2024-11-20 14:27:49.257228428 +0100
> +++ gcc/doc/invoke.texi       2024-11-20 14:44:02.819559242 +0100
> @@ -213,7 +213,9 @@ in the following sections.
>  @item C++ Language Options
>  @xref{C++ Dialect Options,,Options Controlling C++ Dialect}.
>  @gccoptlist{-fabi-version=@var{n}  -fno-access-control
> --faligned-new=@var{n}  -fargs-in-order=@var{n}  -fchar8_t  -fcheck-new
> +-faligned-new=@var{n}  -fargs-in-order=@var{n}
> +-fno-assume-sane-operators-new-delete
> +-fchar8_t  -fcheck-new
>  -fconcepts  -fconstexpr-depth=@var{n}  -fconstexpr-cache-depth=@var{n}
>  -fconstexpr-loop-limit=@var{n}  -fconstexpr-ops-limit=@var{n}
>  -fno-elide-constructors
> @@ -3164,6 +3166,35 @@ but few users will need to override the
>  
>  This flag is enabled by default for @option{-std=c++17}.
>  
> +@opindex fno-assume-sane-operators-new-delete
> +@opindex fassume-sane-operators-new-delete
> +@item -fno-assume-sane-operators-new
> +The C++ standard allows replacing the global @code{new}, @code{new[]},
> +@code{delete} and @code{delete[]} operators, though a lot of C++ programs
> +don't replace them and just use the implementation provided version.
> +Furthermore, the C++ standard allows omitting those calls if they are
> +made from new or delete expressions (and by extension the same is
> +assumed if @code{__builtin_operator_new} or @code{__builtin_operator_delete}
> +functions are used).
> +This option allows control over some optimizations around calls
> +to those operators.
> +With @code{-fassume-sane-operators-new-delete} option GCC may assume that
> +calls to the replaceable global operators from new or delete expressions or
> +from @code{__builtin_operator_new} or @code{__builtin_operator_delete} calls
> +don't read or modify any global variables or variables whose address could
> +escape to the operators (global state; except for @code{errno} for the
> +@code{new} and @code{new[]} operators).
> +This allows most optimizations across those calls and is something that
> +the implementation provided operators satisfy unless @code{malloc}
> +implementation details are observable in the code or unless @code{malloc}
> +hooks are used, but might not be satisfied if a program replaces those
> +operators.  This behavior is enabled by default.
> +With @code{-fno-assume-sane-operators-new-delete} option GCC must
> +assume all these calls (whether from new or delete expressions or called
> +directly) may read and write global state unless proven otherwise (e.g.@:
> +when GCC compiles their implementation).  Use this option if those
> +operators are or may be replaced and code needs to expect such behavior.
> +
>  @opindex fchar8_t
>  @opindex fno-char8_t
>  @item -fchar8_t
> --- gcc/gimple.cc.jj  2024-11-19 12:26:46.270255649 +0100
> +++ gcc/gimple.cc     2024-11-20 14:44:02.820559228 +0100
> @@ -1600,12 +1600,22 @@ gimple_call_fnspec (const gcall *stmt)
>        && DECL_IS_OPERATOR_DELETE_P (fndecl)
>        && DECL_IS_REPLACEABLE_OPERATOR (fndecl)
>        && gimple_call_from_new_or_delete (stmt))
> -    return ". o ";
> +    {
> +      if (flag_assume_sane_operators_new_delete)
> +     return ".co ";
> +      else
> +     return ". o ";
> +    }
>    /* Similarly operator new can be treated as malloc.  */
>    if (fndecl
>        && DECL_IS_REPLACEABLE_OPERATOR_NEW_P (fndecl)
>        && gimple_call_from_new_or_delete (stmt))
> -    return "m ";
> +    {
> +      if (flag_assume_sane_operators_new_delete)
> +     return "mC";
> +      else
> +     return "m ";
> +    }
>    return "";
>  }
>  
> --- gcc/ipa-inline-transform.cc.jj    2024-10-25 10:00:29.478767714 +0200
> +++ gcc/ipa-inline-transform.cc       2024-11-20 14:52:12.062738769 +0100
> @@ -391,17 +391,33 @@ inline_call (struct cgraph_edge *e, bool
>        = DECL_FUNCTION_PERSONALITY (callee->decl);
>  
>    bool reload_optimization_node = false;
> -  if (!opt_for_fn (callee->decl, flag_strict_aliasing)
> -      && opt_for_fn (to->decl, flag_strict_aliasing))
> +  bool remove_strict_aliasing
> +    = (!opt_for_fn (callee->decl, flag_strict_aliasing)
> +       && opt_for_fn (to->decl, flag_strict_aliasing));
> +  bool remove_assume_sane_operators_new_delete
> +    = (!opt_for_fn (callee->decl, flag_assume_sane_operators_new_delete)
> +       && opt_for_fn (to->decl, flag_assume_sane_operators_new_delete));
> +  if (remove_strict_aliasing || remove_assume_sane_operators_new_delete)
>      {
>        struct gcc_options opts = global_options;
>        struct gcc_options opts_set = global_options_set;
>  
>        cl_optimization_restore (&opts, &opts_set, opts_for_fn (to->decl));
> -      opts.x_flag_strict_aliasing = false;
> -      if (dump_file)
> -     fprintf (dump_file, "Dropping flag_strict_aliasing on %s\n",
> -              to->dump_name ());
> +      if (remove_strict_aliasing)
> +     {
> +       opts.x_flag_strict_aliasing = false;
> +       if (dump_file)
> +         fprintf (dump_file, "Dropping flag_strict_aliasing on %s\n",
> +                  to->dump_name ());
> +     }
> +      if (remove_assume_sane_operators_new_delete)
> +     {
> +       opts.x_flag_assume_sane_operators_new_delete = false;
> +       if (dump_file)
> +         fprintf (dump_file,
> +                  "Dropping flag_assume_sane_operators_new_delete on %s\n",
> +                  to->dump_name ());
> +     }
>        DECL_FUNCTION_SPECIFIC_OPTIMIZATION (to->decl)
>        = build_optimization_node (&opts, &opts_set);
>        reload_optimization_node = true;
> --- gcc/c-family/c.opt.jj     2024-11-19 12:26:46.263255748 +0100
> +++ gcc/c-family/c.opt        2024-11-20 14:45:02.092732924 +0100
> @@ -1690,6 +1690,10 @@ fasm
>  C ObjC C++ ObjC++ Var(flag_no_asm, 0)
>  Recognize the \"asm\" keyword.
>  
> +fassume-sane-operators-new-delete
> +C++ ObjC++ Optimization Var(flag_assume_sane_operators_new_delete) Init(1)
> +Assume C++ replaceable global operators new, new[], delete, delete[] don't 
> read or write visible global state.
> +
>  ; Define extra predefined macros for use in libgcc.
>  fbuilding-libgcc
>  C ObjC C++ ObjC++ Undocumented Var(flag_building_libgcc)
> --- gcc/testsuite/g++.dg/tree-ssa/pr110137-1.C.jj     2024-11-20 
> 14:44:02.820559228 +0100
> +++ gcc/testsuite/g++.dg/tree-ssa/pr110137-1.C        2024-11-20 
> 14:44:02.820559228 +0100
> @@ -0,0 +1,74 @@
> +// PR c++/110137
> +// { dg-do compile }
> +// { dg-options "-O2 -fdump-tree-optimized" }
> +// { dg-final { scan-tree-dump "j = 2;" "optimized" } } */
> +// { dg-final { scan-tree-dump "m = 2;" "optimized" } } */
> +// { dg-final { scan-tree-dump-not "q = 2;" "optimized" } } */
> +// { dg-final { scan-tree-dump-not "t = 2;" "optimized" } } */
> +// { dg-final { scan-tree-dump-not "k = 1;" "optimized" } } */
> +// { dg-final { scan-tree-dump-times "r = 1;" 2 "optimized" } } */
> +
> +int i, j, k, l, m, n, o, q, r, s, t, u;
> +
> +void *
> +foo ()
> +{
> +  i = 1;
> +  j = 2;
> +  void *p = ::operator new (32);
> +  j = 3;
> +  k = i;
> +  return p;
> +}
> +
> +void
> +bar (void *p)
> +{
> +  l = 1;
> +  m = 2;
> +  ::operator delete (p);
> +  m = 3;
> +  n = l;
> +}
> +
> +int *
> +baz ()
> +{
> +  o = 1;
> +  q = 2;
> +  int *p = new int;
> +  q = 3;
> +  r = o;
> +  return p;
> +}
> +
> +void
> +qux (int *p)
> +{
> +  s = 1;
> +  t = 2;
> +  delete p;
> +  t = 3;
> +  u = s;
> +}
> +
> +void *
> +corge ()
> +{
> +  o = 1;
> +  q = 2;
> +  void *p = __builtin_operator_new (32);
> +  q = 3;
> +  r = o;
> +  return p;
> +}
> +
> +void
> +waldo (void *p)
> +{
> +  s = 1;
> +  t = 2;
> +  __builtin_operator_delete (p);
> +  t = 3;
> +  u = s;
> +}
> --- gcc/testsuite/g++.dg/tree-ssa/pr110137-2.C.jj     2024-11-20 
> 14:44:02.820559228 +0100
> +++ gcc/testsuite/g++.dg/tree-ssa/pr110137-2.C        2024-11-20 
> 14:44:02.820559228 +0100
> @@ -0,0 +1,74 @@
> +// PR c++/110137
> +// { dg-do compile }
> +// { dg-options "-O2 -fdump-tree-optimized 
> -fassume-sane-operators-new-delete" }
> +// { dg-final { scan-tree-dump "j = 2;" "optimized" } } */
> +// { dg-final { scan-tree-dump "m = 2;" "optimized" } } */
> +// { dg-final { scan-tree-dump-not "q = 2;" "optimized" } } */
> +// { dg-final { scan-tree-dump-not "t = 2;" "optimized" } } */
> +// { dg-final { scan-tree-dump-not "k = 1;" "optimized" } } */
> +// { dg-final { scan-tree-dump-times "r = 1;" 2 "optimized" } } */
> +
> +int i, j, k, l, m, n, o, q, r, s, t, u;
> +
> +void *
> +foo ()
> +{
> +  i = 1;
> +  j = 2;
> +  void *p = ::operator new (32);
> +  j = 3;
> +  k = i;
> +  return p;
> +}
> +
> +void
> +bar (void *p)
> +{
> +  l = 1;
> +  m = 2;
> +  ::operator delete (p);
> +  m = 3;
> +  n = l;
> +}
> +
> +int *
> +baz ()
> +{
> +  o = 1;
> +  q = 2;
> +  int *p = new int;
> +  q = 3;
> +  r = o;
> +  return p;
> +}
> +
> +void
> +qux (int *p)
> +{
> +  s = 1;
> +  t = 2;
> +  delete p;
> +  t = 3;
> +  u = s;
> +}
> +
> +void *
> +corge ()
> +{
> +  o = 1;
> +  q = 2;
> +  void *p = __builtin_operator_new (32);
> +  q = 3;
> +  r = o;
> +  return p;
> +}
> +
> +void
> +waldo (void *p)
> +{
> +  s = 1;
> +  t = 2;
> +  __builtin_operator_delete (p);
> +  t = 3;
> +  u = s;
> +}
> --- gcc/testsuite/g++.dg/tree-ssa/pr110137-3.C.jj     2024-11-20 
> 14:44:02.821559214 +0100
> +++ gcc/testsuite/g++.dg/tree-ssa/pr110137-3.C        2024-11-20 
> 14:44:02.821559214 +0100
> @@ -0,0 +1,76 @@
> +// PR c++/110137
> +// { dg-do compile }
> +// { dg-options "-O2 -fdump-tree-optimized 
> -fno-assume-sane-operators-new-delete" }
> +// { dg-final { scan-tree-dump "j = 2;" "optimized" } } */
> +// { dg-final { scan-tree-dump "m = 2;" "optimized" } } */
> +// { dg-final { scan-tree-dump-times "q = 2;" 2 "optimized" } } */
> +// { dg-final { scan-tree-dump-times "t = 2;" 2 "optimized" } } */
> +// { dg-final { scan-tree-dump-not "k = 1;" "optimized" } } */
> +// { dg-final { scan-tree-dump-not "n = 1;" "optimized" } } */
> +// { dg-final { scan-tree-dump-not "r = 1;" "optimized" } } */
> +// { dg-final { scan-tree-dump-not "u = 1;" "optimized" } } */
> +
> +int i, j, k, l, m, n, o, q, r, s, t, u;
> +
> +void *
> +foo ()
> +{
> +  i = 1;
> +  j = 2;
> +  void *p = ::operator new (32);
> +  j = 3;
> +  k = i;
> +  return p;
> +}
> +
> +void
> +bar (void *p)
> +{
> +  l = 1;
> +  m = 2;
> +  ::operator delete (p);
> +  m = 3;
> +  n = l;
> +}
> +
> +int *
> +baz ()
> +{
> +  o = 1;
> +  q = 2;
> +  int *p = new int;
> +  q = 3;
> +  r = o;
> +  return p;
> +}
> +
> +void
> +qux (int *p)
> +{
> +  s = 1;
> +  t = 2;
> +  delete p;
> +  t = 3;
> +  u = s;
> +}
> +
> +void *
> +corge ()
> +{
> +  o = 1;
> +  q = 2;
> +  void *p = __builtin_operator_new (32);
> +  q = 3;
> +  r = o;
> +  return p;
> +}
> +
> +void
> +waldo (void *p)
> +{
> +  s = 1;
> +  t = 2;
> +  __builtin_operator_delete (p);
> +  t = 3;
> +  u = s;
> +}
> --- gcc/testsuite/g++.dg/tree-ssa/pr110137-4.C.jj     2024-11-20 
> 14:53:59.912235254 +0100
> +++ gcc/testsuite/g++.dg/tree-ssa/pr110137-4.C        2024-11-20 
> 15:00:07.162114734 +0100
> @@ -0,0 +1,124 @@
> +// PR c++/110137
> +// { dg-do compile }
> +// { dg-options "-O2 -fdump-tree-optimized" }
> +// { dg-final { scan-tree-dump "j = 2;" "optimized" } } */
> +// { dg-final { scan-tree-dump "m = 2;" "optimized" } } */
> +// { dg-final { scan-tree-dump-times "q = 2;" 2 "optimized" } } */
> +// { dg-final { scan-tree-dump-times "t = 2;" 2 "optimized" } } */
> +// { dg-final { scan-tree-dump-not "k = 1;" "optimized" } } */
> +// { dg-final { scan-tree-dump-not "n = 1;" "optimized" } } */
> +// { dg-final { scan-tree-dump-not "r = 1;" "optimized" } } */
> +// { dg-final { scan-tree-dump-not "u = 1;" "optimized" } } */
> +
> +int i, j, k, l, m, n, o, q, r, s, t, u;
> +
> +static inline
> +__attribute__((always_inline,
> +            optimize ("no-assume-sane-operators-new-delete"))) void *
> +foo ()
> +{
> +  i = 1;
> +  j = 2;
> +  void *p = ::operator new (32);
> +  j = 3;
> +  k = i;
> +  return p;
> +}
> +
> +static inline
> +__attribute__((always_inline,
> +            optimize ("no-assume-sane-operators-new-delete"))) void
> +bar (void *p)
> +{
> +  l = 1;
> +  m = 2;
> +  ::operator delete (p);
> +  m = 3;
> +  n = l;
> +}
> +
> +static inline
> +__attribute__((always_inline,
> +            optimize ("no-assume-sane-operators-new-delete"))) int *
> +baz ()
> +{
> +  o = 1;
> +  q = 2;
> +  int *p = new int;
> +  q = 3;
> +  r = o;
> +  return p;
> +}
> +
> +static inline
> +__attribute__((always_inline,
> +            optimize ("no-assume-sane-operators-new-delete"))) void
> +qux (int *p)
> +{
> +  s = 1;
> +  t = 2;
> +  delete p;
> +  t = 3;
> +  u = s;
> +}
> +
> +static inline
> +__attribute__((always_inline,
> +            optimize ("no-assume-sane-operators-new-delete"))) void *
> +corge ()
> +{
> +  o = 1;
> +  q = 2;
> +  void *p = __builtin_operator_new (32);
> +  q = 3;
> +  r = o;
> +  return p;
> +}
> +
> +static inline
> +__attribute__((always_inline,
> +            optimize ("no-assume-sane-operators-new-delete"))) void
> +waldo (void *p)
> +{
> +  s = 1;
> +  t = 2;
> +  __builtin_operator_delete (p);
> +  t = 3;
> +  u = s;
> +}
> +
> +void *
> +foo2 ()
> +{
> +  return foo ();
> +}
> +
> +void
> +bar2 (void *p)
> +{
> +  bar (p);
> +}
> +
> +int *
> +baz2 ()
> +{
> +  return baz ();
> +}
> +
> +void
> +qux2 (int *p)
> +{
> +  qux (p);
> +}
> +
> +void *
> +corge2 ()
> +{
> +  return corge ();
> +}
> +
> +void
> +waldo2 (void *p)
> +{
> +  waldo (p);
> +}
> --- gcc/testsuite/g++.dg/torture/pr10148.C.jj 2024-11-19 12:26:46.283255466 
> +0100
> +++ gcc/testsuite/g++.dg/torture/pr10148.C    2024-11-20 14:44:02.848558838 
> +0100
> @@ -1,5 +1,6 @@
>  /* { dg-do run } */
>  /* { dg-skip-if "requires hosted libstdc++ for stdlib malloc" { ! hostedlib 
> } } */
> +/* { dg-additional-options "-fno-assume-sane-operators-new-delete" } */
>  
>  #include <stdlib.h>
>  #include <assert.h>
> --- gcc/testsuite/g++.dg/warn/Warray-bounds-16.C.jj   2023-03-30 
> 16:32:03.824853782 +0200
> +++ gcc/testsuite/g++.dg/warn/Warray-bounds-16.C      2024-11-20 
> 17:37:09.767970243 +0100
> @@ -19,11 +19,11 @@ struct S
>      p = (int*) new unsigned char [sizeof (int) * m];
>  
>      for (int i = 0; i < m; i++)
> -      new (p + i) int (); /* { dg-bogus "bounds" "pr102690" { xfail *-*-* } 
> } */
> +      new (p + i) int ();
>    }
>  };
>  
>  S a (0);
>  
> -/* The loop cannot be eliminated since the global 'new' can change 'm'.  */
> -/* { dg-final { scan-tree-dump-not "goto" "optimized" { xfail *-*-* } } } */
> +/* Verify the loop has been eliminated.
> +   { dg-final { scan-tree-dump-not "goto" "optimized" } } */
> 
> 
>       Jakub
> 
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

Reply via email to