[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