On Fri, Mar 08, 2019 at 02:51:42PM +0100, Richard Biener wrote: > Well, I think having only a single limit is desirable which means we have to > count something resembling the overall work done. We don't have to document > that it matches "statements" (whatever that exactly would be). > > I don't see multi-megabyte constexpr arrays as an issue - do you think we'd > reject them already when parsing them? We could always decide to elide > counting things we didn't do any real work on (like a literal '1' > initializer).
Ok, here is an updated patch that counts cxx_eval_constant_expression calls when: 1) not in stmt skipping mode 2) not CONSTANT_CLASS_P 3) not location_wrapper_p so that if there are huge constexpr array initializers that just contain constants, they aren't counted towards the limit if there are just constants wrapped in location wrappers. The default limit is chosen such that on the new testcase it bails out after about 15 seconds of computation on a fast machine (for a single cxx_eval_outermost_constant_expression). Wanted to find something so that it doesn't trigger too often. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2019-03-15 Jakub Jelinek <ja...@redhat.com> PR c++/87481 * doc/invoke.texi (-fconstexpr-ops-limit=): Document. * c.opt (-fconstexpr-ops-limit=): New option. * constexpr.c (constexpr_ops_count): New variable. (cxx_eval_constant_expression): When not skipping, not constant class or location wrapper, increment constexpr_ops_count and if it is above constexpr_loop_nest_limit, diagnose failure. (cxx_eval_outermost_constant_expr): Clear constexpr_ops_count. * g++.dg/cpp1y/constexpr-87481.C: New test. --- gcc/doc/invoke.texi.jj 2019-03-14 23:44:26.121588028 +0100 +++ gcc/doc/invoke.texi 2019-03-15 19:24:37.428486959 +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-07 20:07:14.890098884 +0100 +++ gcc/c-family/c.opt 2019-03-15 19:22:06.298922976 +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-14 09:12:31.667710331 +0100 +++ gcc/cp/constexpr.c 2019-03-15 19:18:29.559416561 +0100 @@ -4335,6 +4335,11 @@ lookup_placeholder (const constexpr_ctx return ob; } +/* 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; + /* Attempt to reduce the expression T to a constant value. On failure, issue diagnostic and return error_mark_node. */ /* FIXME unify with c_fully_fold */ @@ -4402,6 +4407,20 @@ cxx_eval_constant_expression (const cons return t; } + /* Avoid excessively long constexpr evaluations. */ + if (!location_wrapper_p (t) + && ++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); + constexpr_ops_count = INTTYPE_MINIMUM (HOST_WIDE_INT); + *non_constant_p = true; + return t; + } + tree_code tcode = TREE_CODE (t); switch (tcode) { @@ -5278,6 +5297,7 @@ cxx_eval_outermost_constant_expr (tree t } instantiate_constexpr_fns (r); + constexpr_ops_count = 0; r = cxx_eval_constant_expression (&ctx, r, false, &non_constant_p, &overflow_p); --- gcc/testsuite/g++.dg/cpp1y/constexpr-87481.C.jj 2019-03-15 19:08:21.207253626 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-87481.C 2019-03-15 19:26:10.895980368 +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