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. 2024-11-19 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. 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/torture/pr10148.C: Add -fno-assume-sane-operators-new-delete as dg-additional-options. --- gcc/doc/invoke.texi.jj 2024-11-19 10:23:59.145145887 +0100 +++ gcc/doc/invoke.texi 2024-11-19 12:07:13.942789378 +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 @@ -3163,6 +3165,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-16 10:22:38.386770817 +0100 +++ gcc/gimple.cc 2024-11-19 11:45:33.386136116 +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/c-family/c.opt.jj 2024-11-18 09:05:00.182283546 +0100 +++ gcc/c-family/c.opt 2024-11-19 11:31:29.381038177 +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++ 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. + ; 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-19 11:30:24.866947846 +0100 +++ gcc/testsuite/g++.dg/tree-ssa/pr110137-1.C 2024-11-19 11:52:27.996287689 +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-19 11:30:24.866947846 +0100 +++ gcc/testsuite/g++.dg/tree-ssa/pr110137-2.C 2024-11-19 11:52:18.473422017 +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-19 11:30:24.866947846 +0100 +++ gcc/testsuite/g++.dg/tree-ssa/pr110137-3.C 2024-11-19 11:30:24.866947846 +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/torture/pr10148.C.jj 2024-09-13 16:09:39.744357235 +0200 +++ gcc/testsuite/g++.dg/torture/pr10148.C 2024-11-19 11:59:39.308203533 +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> Jakub