diff mbox series

[C++] Add -fconstexpr-ops-limit= option (PR c++/87481, take 2)

Message ID 20190322125354.GN7611@tucnak
State New
Headers show
Series [C++] Add -fconstexpr-ops-limit= option (PR c++/87481, take 2) | expand

Commit Message

Jakub Jelinek March 22, 2019, 12:53 p.m. UTC
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  <jakub@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.



	Jakub

Comments

Jason Merrill March 22, 2019, 2:27 p.m. UTC | #1
On 3/22/19 8:53 AM, Jakub Jelinek wrote:
> 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?

OK.

>> 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?

Yes.

Jason
diff mbox series

Patch

--- 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" }