On Wed, Dec 1, 2021 at 3:10 AM Jason Merrill via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > > For PR61825, honza changed tree_single_nonzero_warnv_p to prevent a later > declaration from marking a function as weak after we've determined that it > wasn't weak before. But we shouldn't do that for speculative folding; we > should only do it when we actually need a constant value. In C++, such a > context is called "manifestly constant-evaluated". In fold, this seems to > correspond to the folding_initializer flag, since in C this situation only > occurs in static initializers. > > This change makes nonzero-1.c well-formed; I've added a nonzero-1a.c to > verify that we delete the null check eventually if there is no weak > redeclaration. > > The varasm.c change is so that if we do get the weak redeclaration error, we > get it at the position of the weak declaration rather than the previous > declaration. > > Using the FOLD_INIT paths also affects floating point arithmetic: notably, > this makes floating point division by zero in a manifestly > constant-evaluated context constant, as in a C static initializer. I've had > some success convincing CWG that this is the right direction; C++ should > follow C's floating point semantics more than we have been doing, and Joseph > says that the C policy is that Annex F overrides other parts of the standard > that say that some operations are undefined. But since we're in stage 3, > I'm only making this change with the new flag -fconstexpr-fp-except. It may > turn on by default in a future release. > > I think this distinction is only relevant for binary operations; arithmetic > for the floating point case, comparison for possibly non-zero addresses. > > Tested x86_64-pc-linux-gnu, OK for trunk?
OK. Thanks, Richard. > PR c++/103310 > > gcc/ChangeLog: > > * fold-const.c (maybe_nonzero_address): Use get_create or get > depending on folding_initializer. > (fold_binary_initializer_loc): New. > * fold-const.h (fold_binary_initializer_loc): Declare. > * varasm.c (mark_weak): Don't use the decl location. > * doc/invoke.texi: Document -fconstexpr-fp-except. > > gcc/c-family/ChangeLog: > > * c.opt: Add -fconstexpr-fp-except. > > gcc/cp/ChangeLog: > > * constexpr.c (cxx_eval_binary_expression): Use > fold_binary_initializer_loc if manifestly cxeval. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp0x/constexpr-fp-except1.C: New test. > * g++.dg/cpp1z/constexpr-if36.C: New test. > * gcc.dg/tree-ssa/nonzero-1.c: Now well-formed. > * gcc.dg/tree-ssa/nonzero-1a.c: New test. > --- > gcc/doc/invoke.texi | 14 ++++++++++ > gcc/c-family/c.opt | 4 +++ > gcc/fold-const.h | 1 + > gcc/cp/constexpr.c | 9 ++++++- > gcc/fold-const.c | 26 ++++++++++++++++--- > .../g++.dg/cpp0x/constexpr-fp-except1.C | 4 +++ > gcc/testsuite/g++.dg/cpp1z/constexpr-if36.C | 19 ++++++++++++++ > gcc/testsuite/gcc.dg/tree-ssa/nonzero-1.c | 5 ++-- > gcc/testsuite/gcc.dg/tree-ssa/nonzero-1a.c | 11 ++++++++ > gcc/varasm.c | 2 +- > 10 files changed, 88 insertions(+), 7 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-fp-except1.C > create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-if36.C > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/nonzero-1a.c > > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi > index 3bddfbaae6a..d6858d834f9 100644 > --- a/gcc/doc/invoke.texi > +++ b/gcc/doc/invoke.texi > @@ -3035,6 +3035,20 @@ users are likely to want to adjust it, but if your > code does heavy > constexpr calculations you might want to experiment to find which > value works best for you. > > +@item -fconstexpr-fp-except > +@opindex fconstexpr-fp-except > +Annex F of the C standard specifies that IEC559 floating point > +exceptions encountered at compile time should not stop compilation. > +C++ compilers have historically not followed this guidance, instead > +treating floating point division by zero as non-constant even though > +it has a well defined value. This flag tells the compiler to give > +Annex F priority over other rules saying that a particular operation > +is undefined. > + > +@smallexample > +constexpr float inf = 1./0.; // OK with -fconstexpr-fp-except > +@end smallexample > + > @item -fconstexpr-loop-limit=@var{n} > @opindex fconstexpr-loop-limit > Set the maximum number of iterations for a loop in C++14 constexpr functions > diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt > index 4b8a094b206..5654f044ae4 100644 > --- a/gcc/c-family/c.opt > +++ b/gcc/c-family/c.opt > @@ -1615,6 +1615,10 @@ fconstexpr-cache-depth= > C++ ObjC++ Joined RejectNegative UInteger Var(constexpr_cache_depth) Init(8) > -fconstexpr-cache-depth=<number> Specify maximum constexpr recursion > cache depth. > > +fconstexpr-fp-except > +C++ ObjC++ Var(flag_constexpr_fp_except) Init(0) > +Allow IEC559 floating point exceptions in constant expressions > + > fconstexpr-loop-limit= > C++ ObjC++ Joined RejectNegative UInteger Var(constexpr_loop_limit) > Init(262144) > -fconstexpr-loop-limit=<number> Specify maximum constexpr loop > iteration count. > diff --git a/gcc/fold-const.h b/gcc/fold-const.h > index 56e9d399c0d..01bfe5df9b5 100644 > --- a/gcc/fold-const.h > +++ b/gcc/fold-const.h > @@ -77,6 +77,7 @@ extern tree fold_build_call_array_loc (location_t, tree, > tree, int, tree *); > #define fold_build_call_array_initializer(T1,T2,N,T4)\ > fold_build_call_array_initializer_loc (UNKNOWN_LOCATION, T1, T2, N, T4) > extern tree fold_build_call_array_initializer_loc (location_t, tree, tree, > int, tree *); > +extern tree fold_binary_initializer_loc (location_t, tree_code, tree, tree, > tree); > extern tree get_array_ctor_element_at_index (tree, offset_int, > unsigned * = NULL); > extern bool fold_convertible_p (const_tree, const_tree); > diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c > index d66a56583ae..b4b8a96c6af 100644 > --- a/gcc/cp/constexpr.c > +++ b/gcc/cp/constexpr.c > @@ -3366,7 +3366,14 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, > tree t, > } > > if (r == NULL_TREE) > - r = fold_binary_loc (loc, code, type, lhs, rhs); > + { > + if (ctx->manifestly_const_eval > + && (flag_constexpr_fp_except > + || TREE_CODE (type) != REAL_TYPE)) > + r = fold_binary_initializer_loc (loc, code, type, lhs, rhs); > + else > + r = fold_binary_loc (loc, code, type, lhs, rhs); > + } > > if (r == NULL_TREE > && (code == LSHIFT_EXPR || code == RSHIFT_EXPR) > diff --git a/gcc/fold-const.c b/gcc/fold-const.c > index 90d82257ae7..0b9a42f764a 100644 > --- a/gcc/fold-const.c > +++ b/gcc/fold-const.c > @@ -85,8 +85,8 @@ along with GCC; see the file COPYING3. If not see > #include "asan.h" > #include "gimple-range.h" > > -/* Nonzero if we are folding constants inside an initializer; zero > - otherwise. */ > +/* Nonzero if we are folding constants inside an initializer or a C++ > + manifestly-constant-evaluated context; zero otherwise. */ > int folding_initializer = 0; > > /* The following constants represent a bit based encoding of GCC's > @@ -9924,8 +9924,15 @@ pointer_may_wrap_p (tree base, tree offset, poly_int64 > bitpos) > static int > maybe_nonzero_address (tree decl) > { > + /* Normally, don't do anything for variables and functions before symtab is > + built; it is quite possible that DECL will be declared weak later. > + But if folding_initializer, we need a constant answer now, so create > + the symtab entry and prevent later weak declaration. */ > if (DECL_P (decl) && decl_in_symtab_p (decl)) > - if (struct symtab_node *symbol = symtab_node::get_create (decl)) > + if (struct symtab_node *symbol > + = (folding_initializer > + ? symtab_node::get_create (decl) > + : symtab_node::get (decl))) > return symbol->nonzero_address (); > > /* Function local objects are never NULL. */ > @@ -13991,6 +13998,19 @@ fold_build_call_array_initializer_loc (location_t > loc, tree type, tree fn, > return result; > } > > +tree > +fold_binary_initializer_loc (location_t loc, tree_code code, tree type, > + tree lhs, tree rhs) > +{ > + tree result; > + START_FOLD_INIT; > + > + result = fold_binary_loc (loc, code, type, lhs, rhs); > + > + END_FOLD_INIT; > + return result; > +} > + > #undef START_FOLD_INIT > #undef END_FOLD_INIT > > diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-fp-except1.C > b/gcc/testsuite/g++.dg/cpp0x/constexpr-fp-except1.C > new file mode 100644 > index 00000000000..3887a5ba743 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-fp-except1.C > @@ -0,0 +1,4 @@ > +// { dg-do compile { target c++11 } } > +// { dg-additional-options -fconstexpr-fp-except } > + > +constexpr double inf = 1./0.; > diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if36.C > b/gcc/testsuite/g++.dg/cpp1z/constexpr-if36.C > new file mode 100644 > index 00000000000..4a1b134cc19 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-if36.C > @@ -0,0 +1,19 @@ > +// PR c++/103310 > +// Test that only manifestly-constant-evaluated comparisons lock a symbol's > +// weakness. > + > +// { dg-do compile { target c++17 } } > + > +extern void weakfn1 (void); > +extern void weakfn2 (void); > + > +void call_weakfn (void) > +{ > + if (weakfn1) > + weakfn1 (); > + if constexpr (weakfn2) > + weakfn2 (); > +} > + > +extern void weakfn1 (void) __attribute__((weak)); > +extern void weakfn2 (void) __attribute__((weak)); // { dg-error "declared > weak after being used" } > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/nonzero-1.c > b/gcc/testsuite/gcc.dg/tree-ssa/nonzero-1.c > index c9d438e9374..aa21b71c730 100644 > --- a/gcc/testsuite/gcc.dg/tree-ssa/nonzero-1.c > +++ b/gcc/testsuite/gcc.dg/tree-ssa/nonzero-1.c > @@ -1,12 +1,13 @@ > /* { dg-do compile } */ > -/* { dg-options "-O2 -fdelete-null-pointer-checks" } */ > +/* { dg-options "-O2 -fdelete-null-pointer-checks -fdump-tree-optimized" } */ > /* { dg-require-weak "" } */ > > /* { dg-skip-if "" keeps_null_pointer_checks } */ > -extern int a; /* { dg-error "declared weak after being used" } */ > +extern int a; > int > t() > { > + /* { dg-final { scan-tree-dump "&a != 0" "optimized" } } */ > return &a!=0; > } > extern int a __attribute__ ((weak)); > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/nonzero-1a.c > b/gcc/testsuite/gcc.dg/tree-ssa/nonzero-1a.c > new file mode 100644 > index 00000000000..3d1eb97f151 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/nonzero-1a.c > @@ -0,0 +1,11 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdelete-null-pointer-checks -fdump-tree-optimized" } */ > + > +/* { dg-skip-if "" keeps_null_pointer_checks } */ > +extern int a; > +int > +t() > +{ > + /* { dg-final { scan-tree-dump-not "&a != 0" "optimized" } } */ > + return &a!=0; > +} > diff --git a/gcc/varasm.c b/gcc/varasm.c > index d6031d6f1a2..9315e2c6936 100644 > --- a/gcc/varasm.c > +++ b/gcc/varasm.c > @@ -5869,7 +5869,7 @@ mark_weak (tree decl) > > struct symtab_node *n = symtab_node::get (decl); > if (n && n->refuse_visibility_changes) > - error ("%+qD declared weak after being used", decl); > + error ("%qD declared weak after being used", decl); > DECL_WEAK (decl) = 1; > > if (DECL_RTL_SET_P (decl) > > base-commit: a3e75c1491cd2d501081210925a89a65b1c1e5e5 > -- > 2.27.0 >