Author: Timm Baeder Date: 2026-02-09T12:22:48+01:00 New Revision: f71e32196667264607974e22d28d3badb2d73b5e
URL: https://github.com/llvm/llvm-project/commit/f71e32196667264607974e22d28d3badb2d73b5e DIFF: https://github.com/llvm/llvm-project/commit/f71e32196667264607974e22d28d3badb2d73b5e.diff LOG: [clang][bytecode] Implement constexpr step limit (#176150) This only calls `noteStep()` on jump opcodes, so this works for loops. It does not prevent "hangs" when a function is just _very_ long (could be interesting how this interfaces with expand statements?). Fixes https://github.com/llvm/llvm-project/issues/165951 Added: clang/test/AST/ByteCode/constexpr-steps.cpp Modified: clang/lib/AST/ByteCode/Interp.cpp clang/lib/AST/ByteCode/InterpState.cpp clang/lib/AST/ByteCode/InterpState.h Removed: ################################################################################ diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 8eaff4bb07f7d..f6e2f149165ff 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -38,21 +38,21 @@ static bool RetValue(InterpState &S, CodePtr &Pt) { static bool Jmp(InterpState &S, CodePtr &PC, int32_t Offset) { PC += Offset; - return true; + return S.noteStep(PC); } static bool Jt(InterpState &S, CodePtr &PC, int32_t Offset) { if (S.Stk.pop<bool>()) { PC += Offset; } - return true; + return S.noteStep(PC); } static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) { if (!S.Stk.pop<bool>()) { PC += Offset; } - return true; + return S.noteStep(PC); } // https://github.com/llvm/llvm-project/issues/102513 diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp index 837d5fef91b8e..df507bd5507c3 100644 --- a/clang/lib/AST/ByteCode/InterpState.cpp +++ b/clang/lib/AST/ByteCode/InterpState.cpp @@ -20,7 +20,9 @@ using namespace clang::interp; InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk, Context &Ctx, SourceMapper *M) : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(M), P(P), Stk(Stk), - Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame) { + Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame), + StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), + InfiniteSteps(StepsLeft == 0) { InConstantContext = Parent.InConstantContext; CheckingPotentialConstantExpression = Parent.CheckingPotentialConstantExpression; @@ -33,7 +35,8 @@ InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk, : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(nullptr), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this, Func, nullptr, CodePtr(), Func->getArgSize()), - Current(&BottomFrame) { + Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), + InfiniteSteps(StepsLeft == 0) { InConstantContext = Parent.InConstantContext; CheckingPotentialConstantExpression = Parent.CheckingPotentialConstantExpression; @@ -154,3 +157,15 @@ StdAllocatorCaller InterpState::getStdAllocatorCaller(StringRef Name) const { return {}; } + +bool InterpState::noteStep(CodePtr OpPC) { + if (InfiniteSteps) + return true; + + --StepsLeft; + if (StepsLeft != 0) + return true; + + FFDiag(Current->getSource(OpPC), diag::note_constexpr_step_limit_exceeded); + return false; +} diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h index 98dc5cfd3b3c4..83ef56e7f8452 100644 --- a/clang/lib/AST/ByteCode/InterpState.h +++ b/clang/lib/AST/ByteCode/InterpState.h @@ -119,6 +119,10 @@ class InterpState final : public State, public SourceMapper { return Floating(Mem, llvm::APFloatBase::SemanticsToEnum(Sem)); } + /// Note that a step has been executed. If there are no more steps remaining, + /// diagnoses and returns \c false. + bool noteStep(CodePtr OpPC); + private: friend class EvaluationResult; friend class InterpStateCCOverride; @@ -146,6 +150,12 @@ class InterpState final : public State, public SourceMapper { SourceLocation EvalLocation; /// Declaration we're initializing/evaluting, if any. const VarDecl *EvaluatingDecl = nullptr; + /// Steps left during evaluation. + unsigned StepsLeft = 1; + /// Whether infinite evaluation steps have been requested. If this is false, + /// we use the StepsLeft value above. + const bool InfiniteSteps = false; + /// Things needed to do speculative execution. SmallVectorImpl<PartialDiagnosticAt> *PrevDiags = nullptr; unsigned SpeculationDepth = 0; diff --git a/clang/test/AST/ByteCode/constexpr-steps.cpp b/clang/test/AST/ByteCode/constexpr-steps.cpp new file mode 100644 index 0000000000000..490425107a140 --- /dev/null +++ b/clang/test/AST/ByteCode/constexpr-steps.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s -fconstexpr-steps=100 + + +constexpr int foo() { // expected-error {{never produces a constant expression}} + while (1) {} // expected-note 2{{constexpr evaluation hit maximum step limit}} + return 0; +} +static_assert (foo() == 0, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to}} + _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
