On Fri, Mar 22, 2019 at 08:47:21AM -0400, Jason Merrill wrote: > On 3/21/19 7:45 PM, Jakub Jelinek wrote: > > On Thu, Mar 21, 2019 at 07:27:03PM -0400, Jason Merrill wrote: > > > On 3/15/19 4:07 PM, Jakub Jelinek wrote: > > > > +/* Number of cxx_eval_constant_expression calls (except skipped ones, > > > > + on simple constants or location wrappers) encountered during current > > > > + cxx_eval_outermost_constant_expr call. */ > > > > +static HOST_WIDE_INT constexpr_ops_count; > > > > > > Hmm, a global static variable is non-reentrant. This may not be an issue > > > in > > > practice because of instantiate_constexpr_fns, but still seems > > > questionable. > > > > One option would be to add HOST_WIDE_INT *constexpr_ops_count; into > > constexpr_ctx structure (pointer, not the counter itself, because we > > constexpr_ctx new_ctx = *ctx; > > ... (&new_ctx, ...) > > ). > > That makes sense.
I've in the meantime bootstrapped/regtested on x86_64-linux and i686-linux the following patch. Ok for trunk? > I've also wondered about splitting out parts of the context that don't > change so frequently, to avoid adding an extra copy of 'strict' every time > we build a new ctx. I agree it is a good idea. Can that be done incrementally though? 2019-03-22 Jakub Jelinek <ja...@redhat.com> PR c++/87481 * doc/invoke.texi (-fconstexpr-ops-limit=): Document. * c.opt (-fconstexpr-ops-limit=): New option. * constexpr.c (struct constexpr_ctx): Add constexpr_ops_count member. (cxx_eval_constant_expression): When not skipping, not constant class or location wrapper, increment *ctx->constexpr_ops_count and if it is above constexpr_loop_nest_limit, diagnose failure. (cxx_eval_outermost_constant_expr): Add constexpr_ops_count and initialize ctx.constexpr_ops_count to its address. (is_sub_constant_expr): Likewise. * g++.dg/cpp1y/constexpr-87481.C: New test. --- gcc/doc/invoke.texi.jj 2019-03-16 22:17:14.580040411 +0100 +++ gcc/doc/invoke.texi 2019-03-22 08:27:40.990920476 +0100 @@ -210,7 +210,7 @@ in the following sections. @gccoptlist{-fabi-version=@var{n} -fno-access-control @gol -faligned-new=@var{n} -fargs-in-order=@var{n} -fchar8_t -fcheck-new @gol -fconstexpr-depth=@var{n} -fconstexpr-loop-limit=@var{n} @gol --fno-elide-constructors @gol +-fconstexpr-ops-limit=@var{n} -fno-elide-constructors @gol -fno-enforce-eh-specs @gol -fno-gnu-keywords @gol -fno-implicit-templates @gol @@ -2525,6 +2525,16 @@ Set the maximum number of iterations for to @var{n}. A limit is needed to detect infinite loops during constant expression evaluation. The default is 262144 (1<<18). +@item -fconstexpr-ops-limit=@var{n} +@opindex fconstexpr-ops-limit +Set the maximum number of operations during a single constexpr evaluation. +Even when number of iterations of a single loop is limited with the above limit, +if there are several nested loops and each of them has many iterations but still +smaller than the above limit, or if in a body of some loop or even outside +of a loop too many expressions need to be evaluated, the resulting constexpr +evaluation might take too long. +The default is 33554432 (1<<25). + @item -fdeduce-init-list @opindex fdeduce-init-list Enable deduction of a template type parameter as --- gcc/c-family/c.opt.jj 2019-03-16 22:17:14.629039629 +0100 +++ gcc/c-family/c.opt 2019-03-22 08:27:41.007920196 +0100 @@ -1416,6 +1416,10 @@ fconstexpr-loop-limit= C++ ObjC++ Joined RejectNegative UInteger Var(constexpr_loop_limit) Init(262144) -fconstexpr-loop-limit=<number> Specify maximum constexpr loop iteration count. +fconstexpr-ops-limit= +C++ ObjC++ Joined RejectNegative Host_Wide_Int Var(constexpr_ops_limit) Init(33554432) +-fconstexpr-ops-limit=<number> Specify maximum number of constexpr operations during a single constexpr evaluation. + fdebug-cpp C ObjC C++ ObjC++ Emit debug annotations during preprocessing. --- gcc/cp/constexpr.c.jj 2019-03-16 22:17:15.565024702 +0100 +++ gcc/cp/constexpr.c 2019-03-22 08:40:57.501813656 +0100 @@ -1032,6 +1032,11 @@ struct constexpr_ctx { tree object; /* If inside SWITCH_EXPR. */ constexpr_switch_state *css_state; + /* Number of cxx_eval_constant_expression calls (except skipped ones, + on simple constants or location wrappers) encountered during current + cxx_eval_outermost_constant_expr call. */ + HOST_WIDE_INT *constexpr_ops_count; + /* Whether we should error on a non-constant expression or fail quietly. */ bool quiet; /* Whether we are strictly conforming to constant expression rules or @@ -4402,6 +4407,20 @@ cxx_eval_constant_expression (const cons return t; } + /* Avoid excessively long constexpr evaluations. */ + if (!location_wrapper_p (t) + && ++*ctx->constexpr_ops_count >= constexpr_ops_limit) + { + if (!ctx->quiet) + error_at (cp_expr_loc_or_loc (t, input_location), + "%<constexpr%> evaluation operation count exceeds limit of " + "%wd (use -fconstexpr-ops-limit= to increase the limit)", + constexpr_ops_limit); + *ctx->constexpr_ops_count = INTTYPE_MINIMUM (HOST_WIDE_INT); + *non_constant_p = true; + return t; + } + tree_code tcode = TREE_CODE (t); switch (tcode) { @@ -5238,9 +5257,10 @@ cxx_eval_outermost_constant_expr (tree t bool non_constant_p = false; bool overflow_p = false; hash_map<tree,tree> map; + HOST_WIDE_INT constexpr_ctx_count = 0; constexpr_ctx ctx = { NULL, &map, NULL, NULL, NULL, NULL, - allow_non_constant, strict, + &constexpr_ctx_count, allow_non_constant, strict, manifestly_const_eval || !allow_non_constant }; tree type = initialized_type (t); @@ -5382,9 +5402,11 @@ is_sub_constant_expr (tree t) bool non_constant_p = false; bool overflow_p = false; hash_map <tree, tree> map; + HOST_WIDE_INT constexpr_ops_count = 0; constexpr_ctx ctx - = { NULL, &map, NULL, NULL, NULL, NULL, true, true, false }; + = { NULL, &map, NULL, NULL, NULL, NULL, &constexpr_ops_count, + true, true, false }; instantiate_constexpr_fns (t); cxx_eval_constant_expression (&ctx, t, false, &non_constant_p, --- gcc/testsuite/g++.dg/cpp1y/constexpr-87481.C.jj 2019-03-22 08:27:41.008920180 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-87481.C 2019-03-22 08:27:41.008920180 +0100 @@ -0,0 +1,16 @@ +// PR c++/87481 +// { dg-do compile { target c++14 } } +// { dg-options "-fconstexpr-loop-limit=98304 -fconstexpr-ops-limit=131072" } */ + +constexpr unsigned +foo () +{ + unsigned int r = 0; + for (int i = 0; i < 65536; i++) + for (int j = 0; j < 65536; j++) + for (int k = 0; k < 65536; k++) // { dg-error "'constexpr' evaluation operation count exceeds limit of 131072" "" { target *-*-* } 0 } + r += (i + j + k); + return r; +} + +constexpr auto x = foo (); // { dg-message "in 'constexpr' expansion of" } Jakub