[clang] da836b3 - [clang][Interp] Track frame depth

Timm Bäder via cfe-commits cfe-commits at lists.llvm.org
Wed May 31 03:08:18 PDT 2023


Author: Timm Bäder
Date: 2023-05-31T12:08:03+02:00
New Revision: da836b36bc3540d21c947a95474d2bb6cc458951

URL: https://github.com/llvm/llvm-project/commit/da836b36bc3540d21c947a95474d2bb6cc458951
DIFF: https://github.com/llvm/llvm-project/commit/da836b36bc3540d21c947a95474d2bb6cc458951.diff

LOG: [clang][Interp] Track frame depth

Save the depth of each InterpFrame and bail out if we're too deep.

Differential Revision: https://reviews.llvm.org/D148614

Added: 
    clang/test/AST/Interp/depth-limit.cpp
    clang/test/AST/Interp/depth-limit2.cpp

Modified: 
    clang/lib/AST/Interp/Interp.cpp
    clang/lib/AST/Interp/Interp.h
    clang/lib/AST/Interp/InterpFrame.cpp
    clang/lib/AST/Interp/InterpFrame.h
    clang/lib/AST/Interp/InterpState.cpp
    clang/lib/AST/Interp/InterpState.h

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 0479f4c60c16..3798146b32d1 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -341,6 +341,17 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
   return true;
 }
 
+bool CheckCallDepth(InterpState &S, CodePtr OpPC) {
+  if ((S.Current->getDepth() + 1) > S.getLangOpts().ConstexprCallDepth) {
+    S.FFDiag(S.Current->getSource(OpPC),
+             diag::note_constexpr_depth_limit_exceeded)
+        << S.getLangOpts().ConstexprCallDepth;
+    return false;
+  }
+
+  return true;
+}
+
 bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This) {
   if (!This.isZero())
     return true;

diff  --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 64bdd872221a..fd5ce3c32596 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -88,6 +88,10 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
 /// Checks if a method can be called.
 bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F);
 
+/// Checks if calling the currently active function would exceed
+/// the allowed call depth.
+bool CheckCallDepth(InterpState &S, CodePtr OpPC);
+
 /// Checks the 'this' pointer.
 bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
 
@@ -158,7 +162,6 @@ enum class ArithOp { Add, Sub };
 template <PrimType Name, bool Builtin = false,
           class T = typename PrimConv<Name>::T>
 bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
-  S.CallStackDepth--;
   const T &Ret = S.Stk.pop<T>();
 
   assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
@@ -181,8 +184,6 @@ bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
 
 template <bool Builtin = false>
 inline bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) {
-  S.CallStackDepth--;
-
   assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
   if (Builtin || !S.checkingPotentialConstantExpression())
     S.Current->popArgs();
@@ -1598,6 +1599,9 @@ inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func) {
   if (!CheckCallable(S, OpPC, Func))
     return false;
 
+  if (!CheckCallDepth(S, OpPC))
+    return false;
+
   auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC);
   InterpFrame *FrameBefore = S.Current;
   S.Current = NewFrame.get();

diff  --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp
index e20f283c2855..14b55bea8820 100644
--- a/clang/lib/AST/Interp/InterpFrame.cpp
+++ b/clang/lib/AST/Interp/InterpFrame.cpp
@@ -23,8 +23,8 @@ using namespace clang::interp;
 
 InterpFrame::InterpFrame(InterpState &S, const Function *Func,
                          InterpFrame *Caller, CodePtr RetPC)
-    : Caller(Caller), S(S), Func(Func), RetPC(RetPC),
-      ArgSize(Func ? Func->getArgSize() : 0),
+    : Caller(Caller), S(S), Depth(Caller ? Caller->Depth + 1 : 0), Func(Func),
+      RetPC(RetPC), ArgSize(Func ? Func->getArgSize() : 0),
       Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) {
   if (!Func)
     return;

diff  --git a/clang/lib/AST/Interp/InterpFrame.h b/clang/lib/AST/Interp/InterpFrame.h
index c0f4825096be..7988e74a61fe 100644
--- a/clang/lib/AST/Interp/InterpFrame.h
+++ b/clang/lib/AST/Interp/InterpFrame.h
@@ -15,7 +15,6 @@
 
 #include "Frame.h"
 #include "Program.h"
-#include "State.h"
 #include <cstdint>
 #include <vector>
 
@@ -120,6 +119,8 @@ class InterpFrame final : public Frame {
   const Expr *getExpr(CodePtr PC) const;
   SourceLocation getLocation(CodePtr PC) const;
 
+  unsigned getDepth() const { return Depth; }
+
 private:
   /// Returns an original argument from the stack.
   template <typename T> const T &stackRef(unsigned Offset) const {
@@ -145,6 +146,8 @@ class InterpFrame final : public Frame {
 private:
   /// Reference to the interpreter state.
   InterpState &S;
+  /// Depth of this frame.
+  unsigned Depth;
   /// Reference to the function being executed.
   const Function *Func;
   /// Current object pointer for methods.

diff  --git a/clang/lib/AST/Interp/InterpState.cpp b/clang/lib/AST/Interp/InterpState.cpp
index 6ae4ecd78c0f..bd7daf38796c 100644
--- a/clang/lib/AST/Interp/InterpState.cpp
+++ b/clang/lib/AST/Interp/InterpState.cpp
@@ -17,8 +17,7 @@ using namespace clang::interp;
 
 InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk,
                          Context &Ctx, SourceMapper *M)
-    : Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), Current(nullptr),
-      CallStackDepth(Parent.getCallStackDepth() + 1) {}
+    : Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), Current(nullptr) {}
 
 InterpState::~InterpState() {
   while (Current) {

diff  --git a/clang/lib/AST/Interp/InterpState.h b/clang/lib/AST/Interp/InterpState.h
index 033080637385..74c4667bb019 100644
--- a/clang/lib/AST/Interp/InterpState.h
+++ b/clang/lib/AST/Interp/InterpState.h
@@ -15,6 +15,7 @@
 
 #include "Context.h"
 #include "Function.h"
+#include "InterpFrame.h"
 #include "InterpStack.h"
 #include "State.h"
 #include "clang/AST/APValue.h"
@@ -41,7 +42,9 @@ class InterpState final : public State, public SourceMapper {
   // Stack frame accessors.
   Frame *getSplitFrame() { return Parent.getCurrentFrame(); }
   Frame *getCurrentFrame() override;
-  unsigned getCallStackDepth() override { return CallStackDepth; }
+  unsigned getCallStackDepth() override {
+    return Current ? (Current->getDepth() + 1) : 1;
+  }
   const Frame *getBottomFrame() const override {
     return Parent.getBottomFrame();
   }
@@ -103,8 +106,6 @@ class InterpState final : public State, public SourceMapper {
   Context &Ctx;
   /// The current frame.
   InterpFrame *Current = nullptr;
-  /// Call stack depth.
-  unsigned CallStackDepth;
 };
 
 } // namespace interp

diff  --git a/clang/test/AST/Interp/depth-limit.cpp b/clang/test/AST/Interp/depth-limit.cpp
new file mode 100644
index 000000000000..3e8a29c569ce
--- /dev/null
+++ b/clang/test/AST/Interp/depth-limit.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -fconstexpr-depth 100 -verify %s
+// RUN: %clang_cc1 -fconstexpr-depth 100 -verify=ref %s
+
+constexpr int f(int a) {
+  if (a == 100)
+    return 1 / 0; // expected-warning {{division by zero is undefined}} \
+                  // ref-warning {{division by zero is undefined}}
+
+  return f(a + 1); // ref-note {{exceeded maximum depth of 100 calls}} \
+                   // ref-note {{in call to 'f(99)'}} \
+                   // ref-note {{in call to 'f(98)'}} \
+                   // ref-note {{in call to 'f(97)'}} \
+                   // ref-note {{in call to 'f(96)'}} \
+                   // ref-note {{in call to 'f(95)'}} \
+                   // ref-note {{skipping 90 calls in backtrace}} \
+                   // ref-note {{in call to 'f(4)'}} \
+                   // ref-note {{in call to 'f(3)'}} \
+                   // ref-note {{in call to 'f(2)'}} \
+                   // ref-note {{in call to 'f(1)'}} \
+                   // expected-note {{exceeded maximum depth of 100 calls}} \
+                   // expected-note {{in call to 'f(99)'}} \
+                   // expected-note {{in call to 'f(98)'}} \
+                   // expected-note {{in call to 'f(97)'}} \
+                   // expected-note {{in call to 'f(96)'}} \
+                   // expected-note {{in call to 'f(95)'}} \
+                   // expected-note {{skipping 90 calls in backtrace}} \
+                   // expected-note {{in call to 'f(4)'}} \
+                   // expected-note {{in call to 'f(3)'}} \
+                   // expected-note {{in call to 'f(2)'}} \
+                   // expected-note {{in call to 'f(1)'}}
+}
+static_assert(f(0) == 100); // ref-error {{not an integral constant expression}} \
+                            // ref-note {{in call to 'f(0)'}} \
+                            // expected-error {{not an integral constant expression}} \
+                            // expected-note {{in call to 'f(0)'}}

diff  --git a/clang/test/AST/Interp/depth-limit2.cpp b/clang/test/AST/Interp/depth-limit2.cpp
new file mode 100644
index 000000000000..614472c68ba9
--- /dev/null
+++ b/clang/test/AST/Interp/depth-limit2.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -fconstexpr-depth 2 -verify %s
+// RUN: %clang_cc1 -fconstexpr-depth 2 -verify=ref %s
+
+
+constexpr int func() {
+  return 12;
+}
+
+constexpr int foo() {
+  return func(); // expected-note {{exceeded maximum depth of 2 calls}} \
+                 // ref-note {{exceeded maximum depth of 2 calls}}
+}
+
+constexpr int bar() {
+  return foo(); // expected-note {{in call to 'foo()'}} \
+                // ref-note {{in call to 'foo()'}}
+}
+
+static_assert(bar() == 12); // expected-error {{not an integral constant expression}} \
+                            // expected-note {{in call to 'bar()'}} \
+                            // ref-error {{not an integral constant expression}} \
+                            // ref-note {{in call to 'bar()'}}
+


        


More information about the cfe-commits mailing list