diff options
Diffstat (limited to 'clang/lib/AST')
48 files changed, 263 insertions, 8257 deletions
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index dd88c919669..063c202cb63 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12,7 +12,6 @@ #include "clang/AST/ASTContext.h" #include "CXXABI.h" -#include "Interp/Context.h" #include "clang/AST/APValue.h" #include "clang/AST/ASTMutationListener.h" #include "clang/AST/ASTTypeTraits.h" @@ -784,13 +783,6 @@ CXXABI *ASTContext::createCXXABI(const TargetInfo &T) { llvm_unreachable("Invalid CXXABI type!"); } -interp::Context &ASTContext::getInterpContext() { - if (!InterpContext) { - InterpContext.reset(new interp::Context(*this)); - } - return *InterpContext.get(); -} - static const LangASMap *getAddressSpaceMap(const TargetInfo &T, const LangOptions &LOpts) { if (LOpts.FakeAddressSpaceMap) { diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index e2545e80185..64ad029ccea 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -4,8 +4,6 @@ set(LLVM_LINK_COMPONENTS Support ) -add_subdirectory(Interp) - add_clang_library(clangAST APValue.cpp ASTConsumer.cpp @@ -83,6 +81,5 @@ add_clang_library(clangAST LINK_LIBS clangBasic - clangInterp clangLex ) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 415213704be..cc49409412f 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -32,9 +32,6 @@ // //===----------------------------------------------------------------------===// -#include "Interp/Context.h" -#include "Interp/Frame.h" -#include "Interp/State.h" #include "clang/AST/APValue.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" @@ -49,7 +46,6 @@ #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/FixedPoint.h" -#include "clang/Basic/OptionalDiagnostic.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallBitVector.h" @@ -70,8 +66,8 @@ static bool IsGlobalLValue(APValue::LValueBase B); namespace { struct LValue; - class CallStackFrame; - class EvalInfo; + struct CallStackFrame; + struct EvalInfo; using SourceLocExprScopeGuard = CurrentSourceLocExprScope::SourceLocExprScopeGuard; @@ -226,6 +222,12 @@ namespace { return MostDerivedLength; } + // The order of this enum is important for diagnostics. + enum CheckSubobjectKind { + CSK_Base, CSK_Derived, CSK_Field, CSK_ArrayToPointer, CSK_ArrayIndex, + CSK_Real, CSK_Imag + }; + /// A path from a glvalue to a subobject of that glvalue. struct SubobjectDesignator { /// True if the subobject was named in a manner not supported by C++11. Such @@ -478,8 +480,7 @@ namespace { }; /// A stack frame in the constexpr call stack. - class CallStackFrame : public interp::Frame { - public: + struct CallStackFrame { EvalInfo &Info; /// Parent - The caller of this stack frame. @@ -573,12 +574,6 @@ namespace { } APValue &createTemporary(const void *Key, bool IsLifetimeExtended); - - void describe(llvm::raw_ostream &OS) override; - - Frame *getCaller() const override { return Caller; } - SourceLocation getCallLocation() const override { return CallLoc; } - const FunctionDecl *getCallee() const override { return Callee; } }; /// Temporarily override 'this'. @@ -597,6 +592,59 @@ namespace { const LValue *OldThis; }; + /// A partial diagnostic which we might know in advance that we are not going + /// to emit. + class OptionalDiagnostic { + PartialDiagnostic *Diag; + + public: + explicit OptionalDiagnostic(PartialDiagnostic *Diag = nullptr) + : Diag(Diag) {} + + template<typename T> + OptionalDiagnostic &operator<<(const T &v) { + if (Diag) + *Diag << v; + return *this; + } + + OptionalDiagnostic &operator<<(const APSInt &I) { + if (Diag) { + SmallVector<char, 32> Buffer; + I.toString(Buffer); + *Diag << StringRef(Buffer.data(), Buffer.size()); + } + return *this; + } + + OptionalDiagnostic &operator<<(const APFloat &F) { + if (Diag) { + // FIXME: Force the precision of the source value down so we don't + // print digits which are usually useless (we don't really care here if + // we truncate a digit by accident in edge cases). Ideally, + // APFloat::toString would automatically print the shortest + // representation which rounds to the correct value, but it's a bit + // tricky to implement. + unsigned precision = + llvm::APFloat::semanticsPrecision(F.getSemantics()); + precision = (precision * 59 + 195) / 196; + SmallVector<char, 32> Buffer; + F.toString(Buffer, precision); + *Diag << StringRef(Buffer.data(), Buffer.size()); + } + return *this; + } + + OptionalDiagnostic &operator<<(const APFixedPoint &FX) { + if (Diag) { + SmallVector<char, 32> Buffer; + FX.toString(Buffer); + *Diag << StringRef(Buffer.data(), Buffer.size()); + } + return *this; + } + }; + /// A cleanup, and a flag indicating whether it is lifetime-extended. class Cleanup { llvm::PointerIntPair<APValue*, 1, bool> Value; @@ -659,8 +707,7 @@ namespace { /// rules. For example, the RHS of (0 && foo()) is not evaluated. We can /// evaluate the expression regardless of what the RHS is, but C only allows /// certain things in certain situations. - class EvalInfo : public interp::State { - public: + struct EvalInfo { ASTContext &Ctx; /// EvalStatus - Contains information about the evaluation. @@ -680,13 +727,6 @@ namespace { /// we will evaluate. unsigned StepsLeft; - /// Force the use of the experimental new constant interpreter, bailing out - /// with an error if a feature is not supported. - bool ForceNewConstInterp; - - /// Enable the experimental new constant interpreter. - bool EnableNewConstInterp; - /// BottomFrame - The frame in which evaluation started. This must be /// initialized after CurrentCall and CallStackDepth. CallStackFrame BottomFrame; @@ -797,7 +837,7 @@ namespace { /// Are we checking whether the expression is a potential constant /// expression? - bool checkingPotentialConstantExpression() const override { + bool checkingPotentialConstantExpression() const { return EvalMode == EM_PotentialConstantExpression || EvalMode == EM_PotentialConstantExpressionUnevaluated; } @@ -805,28 +845,25 @@ namespace { /// Are we checking an expression for overflow? // FIXME: We should check for any kind of undefined or suspicious behavior // in such constructs, not just overflow. - bool checkingForOverflow() const override { - return EvalMode == EM_EvaluateForOverflow; - } + bool checkingForOverflow() { return EvalMode == EM_EvaluateForOverflow; } EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode) - : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr), - CallStackDepth(0), NextCallIndex(1), - StepsLeft(getLangOpts().ConstexprStepLimit), - ForceNewConstInterp(getLangOpts().ForceNewConstInterp), - EnableNewConstInterp(ForceNewConstInterp || - getLangOpts().EnableNewConstInterp), - BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr), - EvaluatingDecl((const ValueDecl *)nullptr), - EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), - HasFoldFailureDiagnostic(false), InConstantContext(false), - EvalMode(Mode) {} + : Ctx(const_cast<ASTContext &>(C)), EvalStatus(S), CurrentCall(nullptr), + CallStackDepth(0), NextCallIndex(1), + StepsLeft(getLangOpts().ConstexprStepLimit), + BottomFrame(*this, SourceLocation(), nullptr, nullptr, nullptr), + EvaluatingDecl((const ValueDecl *)nullptr), + EvaluatingDeclValue(nullptr), HasActiveDiagnostic(false), + HasFoldFailureDiagnostic(false), + InConstantContext(false), EvalMode(Mode) {} void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) { EvaluatingDecl = Base; EvaluatingDeclValue = &Value; } + const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); } + bool CheckCallLimit(SourceLocation Loc) { // Don't perform any constexpr calls (other than the call we're checking) // when checking a potential constant expression. @@ -870,52 +907,118 @@ namespace { } private: - interp::Frame *getCurrentFrame() override { return CurrentCall; } - const interp::Frame *getBottomFrame() const override { return &BottomFrame; } - - bool hasActiveDiagnostic() override { return HasActiveDiagnostic; } - void setActiveDiagnostic(bool Flag) override { HasActiveDiagnostic = Flag; } - - void setFoldFailureDiagnostic(bool Flag) override { - HasFoldFailureDiagnostic = Flag; - } - - Expr::EvalStatus &getEvalStatus() const override { return EvalStatus; } - - ASTContext &getCtx() const override { return Ctx; } - - // If we have a prior diagnostic, it will be noting that the expression - // isn't a constant expression. This diagnostic is more important, - // unless we require this evaluation to produce a constant expression. - // - // FIXME: We might want to show both diagnostics to the user in - // EM_ConstantFold mode. - bool hasPriorDiagnostic() override { - if (!EvalStatus.Diag->empty()) { - switch (EvalMode) { - case EM_ConstantFold: - case EM_IgnoreSideEffects: - case EM_EvaluateForOverflow: - if (!HasFoldFailureDiagnostic) - break; - // We've already failed to fold something. Keep that diagnostic. - LLVM_FALLTHROUGH; - case EM_ConstantExpression: - case EM_PotentialConstantExpression: - case EM_ConstantExpressionUnevaluated: - case EM_PotentialConstantExpressionUnevaluated: - setActiveDiagnostic(false); - return true; + /// Add a diagnostic to the diagnostics list. + PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId) { + PartialDiagnostic PD(DiagId, Ctx.getDiagAllocator()); + EvalStatus.Diag->push_back(std::make_pair(Loc, PD)); + return EvalStatus.Diag->back().second; + } + + /// Add notes containing a call stack to the current point of evaluation. + void addCallStack(unsigned Limit); + + private: + OptionalDiagnostic Diag(SourceLocation Loc, diag::kind DiagId, + unsigned ExtraNotes, bool IsCCEDiag) { + + if (EvalStatus.Diag) { + // If we have a prior diagnostic, it will be noting that the expression + // isn't a constant expression. This diagnostic is more important, + // unless we require this evaluation to produce a constant expression. + // + // FIXME: We might want to show both diagnostics to the user in + // EM_ConstantFold mode. + if (!EvalStatus.Diag->empty()) { + switch (EvalMode) { + case EM_ConstantFold: + case EM_IgnoreSideEffects: + case EM_EvaluateForOverflow: + if (!HasFoldFailureDiagnostic) + break; + // We've already failed to fold something. Keep that diagnostic. + LLVM_FALLTHROUGH; + case EM_ConstantExpression: + case EM_PotentialConstantExpression: + case EM_ConstantExpressionUnevaluated: + case EM_PotentialConstantExpressionUnevaluated: + HasActiveDiagnostic = false; + return OptionalDiagnostic(); + } } + + unsigned CallStackNotes = CallStackDepth - 1; + unsigned Limit = Ctx.getDiagnostics().getConstexprBacktraceLimit(); + if (Limit) + CallStackNotes = std::min(CallStackNotes, Limit + 1); + if (checkingPotentialConstantExpression()) + CallStackNotes = 0; + + HasActiveDiagnostic = true; + HasFoldFailureDiagnostic = !IsCCEDiag; + EvalStatus.Diag->clear(); + EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes); + addDiag(Loc, DiagId); + if (!checkingPotentialConstantExpression()) + addCallStack(Limit); + return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second); } - return false; + HasActiveDiagnostic = false; + return OptionalDiagnostic(); + } + public: + // Diagnose that the evaluation could not be folded (FF => FoldFailure) + OptionalDiagnostic + FFDiag(SourceLocation Loc, + diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, + unsigned ExtraNotes = 0) { + return Diag(Loc, DiagId, ExtraNotes, false); } - unsigned getCallStackDepth() override { - return CallStackDepth; + OptionalDiagnostic FFDiag(const Expr *E, diag::kind DiagId + = diag::note_invalid_subexpr_in_const_expr, + unsigned ExtraNotes = 0) { + if (EvalStatus.Diag) + return Diag(E->getExprLoc(), DiagId, ExtraNotes, /*IsCCEDiag*/false); + HasActiveDiagnostic = false; + return OptionalDiagnostic(); + } + + /// Diagnose that the evaluation does not produce a C++11 core constant + /// expression. + /// + /// FIXME: Stop evaluating if we're in EM_ConstantExpression or + /// EM_PotentialConstantExpression mode and we produce one of these. + OptionalDiagnostic CCEDiag(SourceLocation Loc, diag::kind DiagId + = diag::note_invalid_subexpr_in_const_expr, + unsigned ExtraNotes = 0) { + // Don't override a previous diagnostic. Don't bother collecting + // diagnostics if we're evaluating for overflow. + if (!EvalStatus.Diag || !EvalStatus.Diag->empty()) { + HasActiveDiagnostic = false; + return OptionalDiagnostic(); + } + return Diag(Loc, DiagId, ExtraNotes, true); + } + OptionalDiagnostic CCEDiag(const Expr *E, diag::kind DiagId + = diag::note_invalid_subexpr_in_const_expr, + unsigned ExtraNotes = 0) { + return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes); + } + /// Add a note to a prior diagnostic. + OptionalDiagnostic Note(SourceLocation Loc, diag::kind DiagId) { + if (!HasActiveDiagnostic) + return OptionalDiagnostic(); + return OptionalDiagnostic(&addDiag(Loc, DiagId)); + } + + /// Add a stack of notes to a prior diagnostic. + void addNotes(ArrayRef<PartialDiagnosticAt> Diags) { + if (HasActiveDiagnostic) { + EvalStatus.Diag->insert(EvalStatus.Diag->end(), + Diags.begin(), Diags.end()); + } } - public: /// Should we continue evaluation after encountering a side-effect that we /// couldn't model? bool keepEvaluatingAfterSideEffect() { @@ -961,14 +1064,14 @@ namespace { /// Note that we hit something that was technically undefined behavior, but /// that we can evaluate past it (such as signed overflow or floating-point /// division by zero.) - bool noteUndefinedBehavior() override { + bool noteUndefinedBehavior() { EvalStatus.HasUndefinedBehavior = true; return keepEvaluatingAfterUndefinedBehavior(); } /// Should we continue evaluation as much as possible after encountering a /// construct which can't be reduced to a value? - bool keepEvaluatingAfterFailure() const override { + bool keepEvaluatingAfterFailure() { if (!StepsLeft) return false; @@ -1218,6 +1321,62 @@ APValue &CallStackFrame::createTemporary(const void *Key, return Result; } +static void describeCall(CallStackFrame *Frame, raw_ostream &Out); + +void EvalInfo::addCallStack(unsigned Limit) { + // Determine which calls to skip, if any. + unsigned ActiveCalls = CallStackDepth - 1; + unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart; + if (Limit && Limit < ActiveCalls) { + SkipStart = Limit / 2 + Limit % 2; + SkipEnd = ActiveCalls - Limit / 2; + } + + // Walk the call stack and add the diagnostics. + unsigned CallIdx = 0; + for (CallStackFrame *Frame = CurrentCall; Frame != &BottomFrame; + Frame = Frame->Caller, ++CallIdx) { + // Skip this call? + if (CallIdx >= SkipStart && CallIdx < SkipEnd) { + if (CallIdx == SkipStart) { + // Note that we're skipping calls. + addDiag(Frame->CallLoc, diag::note_constexpr_calls_suppressed) + << unsigned(ActiveCalls - Limit); + } + continue; + } + + // Use a different note for an inheriting constructor, because from the + // user's perspective it's not really a function at all. + if (auto *CD = dyn_cast_or_null<CXXConstructorDecl>(Frame->Callee)) { + if (CD->isInheritingConstructor()) { + addDiag(Frame->CallLoc, diag::note_constexpr_inherited_ctor_call_here) + << CD->getParent(); + continue; + } + } + + SmallVector<char, 128> Buffer; + llvm::raw_svector_ostream Out(Buffer); + describeCall(Frame, Out); + addDiag(Frame->CallLoc, diag::note_constexpr_call_here) << Out.str(); + } +} + +/// Kinds of access we can perform on an object, for diagnostics. Note that +/// we consider a member function call to be a kind of access, even though +/// it is not formally an access of the object, because it has (largely) the +/// same set of semantic restrictions. +enum AccessKinds { + AK_Read, + AK_Assign, + AK_Increment, + AK_Decrement, + AK_MemberCall, + AK_DynamicCast, + AK_TypeId, +}; + static bool isModification(AccessKinds AK) { switch (AK) { case AK_Read: @@ -1585,36 +1744,36 @@ static void negateAsSigned(APSInt &Int) { } /// Produce a string describing the given constexpr call. -void CallStackFrame::describe(raw_ostream &Out) { +static void describeCall(CallStackFrame *Frame, raw_ostream &Out) { unsigned ArgIndex = 0; - bool IsMemberCall = isa<CXXMethodDecl>(Callee) && - !isa<CXXConstructorDecl>(Callee) && - cast<CXXMethodDecl>(Callee)->isInstance(); + bool IsMemberCall = isa<CXXMethodDecl>(Frame->Callee) && + !isa<CXXConstructorDecl>(Frame->Callee) && + cast<CXXMethodDecl>(Frame->Callee)->isInstance(); if (!IsMemberCall) - Out << *Callee << '('; + Out << *Frame->Callee << '('; - if (This && IsMemberCall) { + if (Frame->This && IsMemberCall) { APValue Val; - This->moveInto(Val); - Val.printPretty(Out, Info.Ctx, - This->Designator.MostDerivedType); + Frame->This->moveInto(Val); + Val.printPretty(Out, Frame->Info.Ctx, + Frame->This->Designator.MostDerivedType); // FIXME: Add parens around Val if needed. - Out << "->" << *Callee << '('; + Out << "->" << *Frame->Callee << '('; IsMemberCall = false; } - for (FunctionDecl::param_const_iterator I = Callee->param_begin(), - E = Callee->param_end(); I != E; ++I, ++ArgIndex) { + for (FunctionDecl::param_const_iterator I = Frame->Callee->param_begin(), + E = Frame->Callee->param_end(); I != E; ++I, ++ArgIndex) { if (ArgIndex > (unsigned)IsMemberCall) Out << ", "; const ParmVarDecl *Param = *I; - const APValue &Arg = Arguments[ArgIndex]; - Arg.printPretty(Out, Info.Ctx, Param->getType()); + const APValue &Arg = Frame->Arguments[ArgIndex]; + Arg.printPretty(Out, Frame->Info.Ctx, Param->getType()); if (ArgIndex == 0 && IsMemberCall) - Out << "->" << *Callee << '('; + Out << "->" << *Frame->Callee << '('; } Out << ')'; @@ -12117,18 +12276,6 @@ static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This, /// EvaluateAsRValue - Try to evaluate this expression, performing an implicit /// lvalue-to-rvalue cast if it is an lvalue. static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { - if (Info.EnableNewConstInterp) { - auto &InterpCtx = Info.Ctx.getInterpContext(); - switch (InterpCtx.evaluateAsRValue(Info, E, Result)) { - case interp::InterpResult::Success: - return true; - case interp::InterpResult::Fail: - return false; - case interp::InterpResult::Bail: - break; - } - } - if (E->getType().isNull()) return false; @@ -12336,29 +12483,11 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, Expr::EvalStatus EStatus; EStatus.Diag = &Notes; - EvalInfo Info(Ctx, EStatus, VD->isConstexpr() + EvalInfo InitInfo(Ctx, EStatus, VD->isConstexpr() ? EvalInfo::EM_ConstantExpression : EvalInfo::EM_ConstantFold); - Info.setEvaluatingDecl(VD, Value); - Info.InConstantContext = true; - - SourceLocation DeclLoc = VD->getLocation(); - QualType DeclTy = VD->getType(); - - if (Info.EnableNewConstInterp) { - auto &InterpCtx = const_cast<ASTContext &>(Ctx).getInterpContext(); - switch (InterpCtx.evaluateAsInitializer(Info, VD, Value)) { - case interp::InterpResult::Fail: - // Bail out if an error was encountered. - return false; - case interp::InterpResult::Success: - // Evaluation succeeded and value was set. - return CheckConstantExpression(Info, DeclLoc, DeclTy, Value); - case interp::InterpResult::Bail: - // Evaluate the value again for the tree evaluator to use. - break; - } - } + InitInfo.setEvaluatingDecl(VD, Value); + InitInfo.InConstantContext = true; LValue LVal; LVal.set(VD); @@ -12368,19 +12497,20 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx, // zero-initialized before any other initialization takes place. // This behavior is not present in C. if (Ctx.getLangOpts().CPlusPlus && !VD->hasLocalStorage() && - !DeclTy->isReferenceType()) { - ImplicitValueInitExpr VIE(DeclTy); - if (!EvaluateInPlace(Value, Info, LVal, &VIE, + !VD->getType()->isReferenceType()) { + ImplicitValueInitExpr VIE(VD->getType()); + if (!EvaluateInPlace(Value, InitInfo, LVal, &VIE, /*AllowNonLiteralTypes=*/true)) return false; } - if (!EvaluateInPlace(Value, Info, LVal, this, + if (!EvaluateInPlace(Value, InitInfo, LVal, this, /*AllowNonLiteralTypes=*/true) || EStatus.HasSideEffects) return false; - return CheckConstantExpression(Info, DeclLoc, DeclTy, Value); + return CheckConstantExpression(InitInfo, VD->getLocation(), VD->getType(), + Value); } /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be @@ -13055,18 +13185,6 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, EvalInfo::EM_PotentialConstantExpression); Info.InConstantContext = true; - // The constexpr VM attempts to compile all methods to bytecode here. - if (Info.EnableNewConstInterp) { - auto &InterpCtx = Info.Ctx.getInterpContext(); - switch (InterpCtx.isPotentialConstantExpr(Info, FD)) { - case interp::InterpResult::Success: - case interp::InterpResult::Fail: - return Diags.empty(); - case interp::InterpResult::Bail: - break; - } - } - const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD); const CXXRecordDecl *RD = MD ? MD->getParent()->getCanonicalDecl() : nullptr; diff --git a/clang/lib/AST/Interp/Block.cpp b/clang/lib/AST/Interp/Block.cpp deleted file mode 100644 index 5fc93eb39f4..00000000000 --- a/clang/lib/AST/Interp/Block.cpp +++ /dev/null @@ -1,87 +0,0 @@ -//===--- Block.cpp - Allocated blocks for the interpreter -------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the classes describing allocated blocks. -// -//===----------------------------------------------------------------------===// - -#include "Block.h" -#include "Pointer.h" - -using namespace clang; -using namespace clang::interp; - - - -void Block::addPointer(Pointer *P) { - if (IsStatic) - return; - if (Pointers) - Pointers->Prev = P; - P->Next = Pointers; - P->Prev = nullptr; - Pointers = P; -} - -void Block::removePointer(Pointer *P) { - if (IsStatic) - return; - if (Pointers == P) - Pointers = P->Next; - if (P->Prev) - P->Prev->Next = P->Next; - if (P->Next) - P->Next->Prev = P->Prev; -} - -void Block::cleanup() { - if (Pointers == nullptr && IsDead) - (reinterpret_cast<DeadBlock *>(this + 1) - 1)->free(); -} - -void Block::movePointer(Pointer *From, Pointer *To) { - if (IsStatic) - return; - To->Prev = From->Prev; - if (To->Prev) - To->Prev->Next = To; - To->Next = From->Next; - if (To->Next) - To->Next->Prev = To; - if (Pointers == From) - Pointers = To; - - From->Prev = nullptr; - From->Next = nullptr; -} - -DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk) - : Root(Root), B(Blk->Desc, Blk->IsStatic, Blk->IsExtern, /*isDead=*/true) { - // Add the block to the chain of dead blocks. - if (Root) - Root->Prev = this; - - Next = Root; - Prev = nullptr; - Root = this; - - // Transfer pointers. - B.Pointers = Blk->Pointers; - for (Pointer *P = Blk->Pointers; P; P = P->Next) - P->Pointee = &B; -} - -void DeadBlock::free() { - if (Prev) - Prev->Next = Next; - if (Next) - Next->Prev = Prev; - if (Root == this) - Root = Next; - ::free(this); -} diff --git a/clang/lib/AST/Interp/Block.h b/clang/lib/AST/Interp/Block.h deleted file mode 100644 index 97fb9a3ca09..00000000000 --- a/clang/lib/AST/Interp/Block.h +++ /dev/null @@ -1,140 +0,0 @@ -//===--- Block.h - Allocated blocks for the interpreter ---------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the classes describing allocated blocks. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_BLOCK_H -#define LLVM_CLANG_AST_INTERP_BLOCK_H - -#include "Descriptor.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ComparisonCategories.h" -#include "llvm/ADT/PointerUnion.h" -#include "llvm/Support/raw_ostream.h" - -namespace clang { -namespace interp { -class Block; -class DeadBlock; -class Context; -class InterpState; -class Pointer; -class Function; -enum PrimType : unsigned; - -/// A memory block, either on the stack or in the heap. -/// -/// The storage described by the block immediately follows it in memory. -class Block { -public: - // Creates a new block. - Block(const llvm::Optional<unsigned> &DeclID, Descriptor *Desc, - bool IsStatic = false, bool IsExtern = false) - : DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {} - - Block(Descriptor *Desc, bool IsStatic = false, bool IsExtern = false) - : DeclID((unsigned)-1), IsStatic(IsStatic), IsExtern(IsExtern), - Desc(Desc) {} - - /// Returns the block's descriptor. - Descriptor *getDescriptor() const { return Desc; } - /// Checks if the block has any live pointers. - bool hasPointers() const { return Pointers; } - /// Checks if the block is extern. - bool isExtern() const { return IsExtern; } - /// Checks if the block has static storage duration. - bool isStatic() const { return IsStatic; } - /// Checks if the block is temporary. - bool isTemporary() const { return Desc->IsTemporary; } - /// Returns the size of the block. - InterpSize getSize() const { return Desc->getAllocSize(); } - /// Returns the declaration ID. - llvm::Optional<unsigned> getDeclID() const { return DeclID; } - - /// Returns a pointer to the stored data. - char *data() { return reinterpret_cast<char *>(this + 1); } - - /// Returns a view over the data. - template <typename T> - T &deref() { return *reinterpret_cast<T *>(data()); } - - /// Invokes the constructor. - void invokeCtor() { - std::memset(data(), 0, getSize()); - if (Desc->CtorFn) - Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable, - /*isActive=*/true, Desc); - } - -protected: - friend class Pointer; - friend class DeadBlock; - friend class InterpState; - - Block(Descriptor *Desc, bool IsExtern, bool IsStatic, bool IsDead) - : IsStatic(IsStatic), IsExtern(IsExtern), IsDead(true), Desc(Desc) {} - - // Deletes a dead block at the end of its lifetime. - void cleanup(); - - // Pointer chain management. - void addPointer(Pointer *P); - void removePointer(Pointer *P); - void movePointer(Pointer *From, Pointer *To); - - /// Start of the chain of pointers. - Pointer *Pointers = nullptr; - /// Unique identifier of the declaration. - llvm::Optional<unsigned> DeclID; - /// Flag indicating if the block has static storage duration. - bool IsStatic = false; - /// Flag indicating if the block is an extern. - bool IsExtern = false; - /// Flag indicating if the pointer is dead. - bool IsDead = false; - /// Pointer to the stack slot descriptor. - Descriptor *Desc; -}; - -/// Descriptor for a dead block. -/// -/// Dead blocks are chained in a double-linked list to deallocate them -/// whenever pointers become dead. -class DeadBlock { -public: - /// Copies the block. - DeadBlock(DeadBlock *&Root, Block *Blk); - - /// Returns a pointer to the stored data. - char *data() { return B.data(); } - -private: - friend class Block; - friend class InterpState; - - void free(); - - /// Root pointer of the list. - DeadBlock *&Root; - /// Previous block in the list. - DeadBlock *Prev; - /// Next block in the list. - DeadBlock *Next; - - /// Actual block storing data and tracking pointers. - Block B; -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp deleted file mode 100644 index 7a4569820a1..00000000000 --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ /dev/null @@ -1,175 +0,0 @@ -//===--- ByteCodeEmitter.cpp - Instruction emitter for the VM ---*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "ByteCodeEmitter.h" -#include "Context.h" -#include "Opcode.h" -#include "Program.h" -#include "clang/AST/DeclCXX.h" - -using namespace clang; -using namespace clang::interp; - -using APSInt = llvm::APSInt; -using Error = llvm::Error; - -Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) { - // Do not try to compile undefined functions. - if (!F->isDefined(F) || (!F->hasBody() && F->willHaveBody())) - return nullptr; - - // Set up argument indices. - unsigned ParamOffset = 0; - SmallVector<PrimType, 8> ParamTypes; - llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors; - - // If the return is not a primitive, a pointer to the storage where the value - // is initialized in is passed as the first argument. - QualType Ty = F->getReturnType(); - if (!Ty->isVoidType() && !Ctx.classify(Ty)) { - ParamTypes.push_back(PT_Ptr); - ParamOffset += align(primSize(PT_Ptr)); - } - - // Assign descriptors to all parameters. - // Composite objects are lowered to pointers. - for (const ParmVarDecl *PD : F->parameters()) { - PrimType Ty; - if (llvm::Optional<PrimType> T = Ctx.classify(PD->getType())) { - Ty = *T; - } else { - Ty = PT_Ptr; - } - - Descriptor *Desc = P.createDescriptor(PD, Ty); - ParamDescriptors.insert({ParamOffset, {Ty, Desc}}); - Params.insert({PD, ParamOffset}); - ParamOffset += align(primSize(Ty)); - ParamTypes.push_back(Ty); - } - - // Create a handle over the emitted code. - Function *Func = P.createFunction(F, ParamOffset, std::move(ParamTypes), - std::move(ParamDescriptors)); - // Compile the function body. - if (!F->isConstexpr() || !visitFunc(F)) { - // Return a dummy function if compilation failed. - if (BailLocation) - return llvm::make_error<ByteCodeGenError>(*BailLocation); - else - return Func; - } else { - // Create scopes from descriptors. - llvm::SmallVector<Scope, 2> Scopes; - for (auto &DS : Descriptors) { - Scopes.emplace_back(std::move(DS)); - } - - // Set the function's code. - Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap), - std::move(Scopes)); - return Func; - } -} - -Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) { - NextLocalOffset += sizeof(Block); - unsigned Location = NextLocalOffset; - NextLocalOffset += align(D->getAllocSize()); - return {Location, D}; -} - -void ByteCodeEmitter::emitLabel(LabelTy Label) { - const size_t Target = Code.size(); - LabelOffsets.insert({Label, Target}); - auto It = LabelRelocs.find(Label); - if (It != LabelRelocs.end()) { - for (unsigned Reloc : It->second) { - using namespace llvm::support; - - /// Rewrite the operand of all jumps to this label. - void *Location = Code.data() + Reloc - sizeof(int32_t); - const int32_t Offset = Target - static_cast<int64_t>(Reloc); - endian::write<int32_t, endianness::native, 1>(Location, Offset); - } - LabelRelocs.erase(It); - } -} - -int32_t ByteCodeEmitter::getOffset(LabelTy Label) { - // Compute the PC offset which the jump is relative to. - const int64_t Position = Code.size() + sizeof(Opcode) + sizeof(int32_t); - - // If target is known, compute jump offset. - auto It = LabelOffsets.find(Label); - if (It != LabelOffsets.end()) { - return It->second - Position; - } - - // Otherwise, record relocation and return dummy offset. - LabelRelocs[Label].push_back(Position); - return 0ull; -} - -bool ByteCodeEmitter::bail(const SourceLocation &Loc) { - if (!BailLocation) - BailLocation = Loc; - return false; -} - -template <typename... Tys> -bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) { - bool Success = true; - - /// Helper to write bytecode and bail out if 32-bit offsets become invalid. - auto emit = [this, &Success](const char *Data, size_t Size) { - if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { - Success = false; - return; - } - Code.insert(Code.end(), Data, Data + Size); - }; - - /// The opcode is followed by arguments. The source info is - /// attached to the address after the opcode. - emit(reinterpret_cast<const char *>(&Op), sizeof(Opcode)); - if (SI) - SrcMap.emplace_back(Code.size(), SI); - - /// The initializer list forces the expression to be evaluated - /// for each argument in the variadic template, in order. - (void)std::initializer_list<int>{ - (emit(reinterpret_cast<const char *>(&Args), sizeof(Args)), 0)...}; - - return Success; -} - -bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) { - return emitJt(getOffset(Label), SourceInfo{}); -} - -bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) { - return emitJf(getOffset(Label), SourceInfo{}); -} - -bool ByteCodeEmitter::jump(const LabelTy &Label) { - return emitJmp(getOffset(Label), SourceInfo{}); -} - -bool ByteCodeEmitter::fallthrough(const LabelTy &Label) { - emitLabel(Label); - return true; -} - -//===----------------------------------------------------------------------===// -// Opcode emitters -//===----------------------------------------------------------------------===// - -#define GET_LINK_IMPL -#include "Opcodes.inc" -#undef GET_LINK_IMPL diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.h b/clang/lib/AST/Interp/ByteCodeEmitter.h deleted file mode 100644 index b2006ef24cb..00000000000 --- a/clang/lib/AST/Interp/ByteCodeEmitter.h +++ /dev/null @@ -1,112 +0,0 @@ -//===--- ByteCodeEmitter.h - Instruction emitter for the VM ---------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the instruction emitters. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_LINKEMITTER_H -#define LLVM_CLANG_AST_INTERP_LINKEMITTER_H - -#include "ByteCodeGenError.h" -#include "Context.h" -#include "InterpStack.h" -#include "InterpState.h" -#include "Program.h" -#include "Source.h" -#include "Type.h" -#include "llvm/Support/Error.h" - -namespace clang { -namespace interp { -class Context; -class SourceInfo; -enum Opcode : uint32_t; - -/// An emitter which links the program to bytecode for later use. -class ByteCodeEmitter { -protected: - using LabelTy = uint32_t; - using AddrTy = uintptr_t; - using Local = Scope::Local; - -public: - /// Compiles the function into the module. - llvm::Expected<Function *> compileFunc(const FunctionDecl *F); - -protected: - ByteCodeEmitter(Context &Ctx, Program &P) : Ctx(Ctx), P(P) {} - - virtual ~ByteCodeEmitter() {} - - /// Define a label. - void emitLabel(LabelTy Label); - /// Create a label. - LabelTy getLabel() { return ++NextLabel; } - - /// Methods implemented by the compiler. - virtual bool visitFunc(const FunctionDecl *E) = 0; - virtual bool visitExpr(const Expr *E) = 0; - virtual bool visitDecl(const VarDecl *E) = 0; - - /// Bails out if a given node cannot be compiled. - bool bail(const Stmt *S) { return bail(S->getBeginLoc()); } - bool bail(const Decl *D) { return bail(D->getBeginLoc()); } - bool bail(const SourceLocation &Loc); - - /// Emits jumps. - bool jumpTrue(const LabelTy &Label); - bool jumpFalse(const LabelTy &Label); - bool jump(const LabelTy &Label); - bool fallthrough(const LabelTy &Label); - - /// Callback for local registration. - Local createLocal(Descriptor *D); - - /// Parameter indices. - llvm::DenseMap<const ParmVarDecl *, unsigned> Params; - /// Local descriptors. - llvm::SmallVector<SmallVector<Local, 8>, 2> Descriptors; - -private: - /// Current compilation context. - Context &Ctx; - /// Program to link to. - Program &P; - /// Index of the next available label. - LabelTy NextLabel = 0; - /// Offset of the next local variable. - unsigned NextLocalOffset = 0; - /// Location of a failure. - llvm::Optional<SourceLocation> BailLocation; - /// Label information for linker. - llvm::DenseMap<LabelTy, unsigned> LabelOffsets; - /// Location of label relocations. - llvm::DenseMap<LabelTy, llvm::SmallVector<unsigned, 5>> LabelRelocs; - /// Program code. - std::vector<char> Code; - /// Opcode to expression mapping. - SourceMap SrcMap; - - /// Returns the offset for a jump or records a relocation. - int32_t getOffset(LabelTy Label); - - /// Emits an opcode. - template <typename... Tys> - bool emitOp(Opcode Op, const Tys &... Args, const SourceInfo &L); - -protected: -#define GET_LINK_PROTO -#include "Opcodes.inc" -#undef GET_LINK_PROTO -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp deleted file mode 100644 index 6c34f5d8f3d..00000000000 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ /dev/null @@ -1,580 +0,0 @@ -//===--- ByteCodeExprGen.cpp - Code generator for expressions ---*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "ByteCodeExprGen.h" -#include "ByteCodeEmitter.h" -#include "ByteCodeGenError.h" -#include "Context.h" -#include "Function.h" -#include "Program.h" -#include "State.h" -#include "Type.h" - -using namespace clang; -using namespace clang::interp; - -using APSInt = llvm::APSInt; -template <typename T> using Expected = llvm::Expected<T>; -template <typename T> using Optional = llvm::Optional<T>; - -namespace clang { -namespace interp { - -/// Scope used to handle temporaries in toplevel variable declarations. -template <class Emitter> class DeclScope final : public LocalScope<Emitter> { -public: - DeclScope(ByteCodeExprGen<Emitter> *Ctx, const VarDecl *VD) - : LocalScope<Emitter>(Ctx), Scope(Ctx->P, VD) {} - - void addExtended(const Scope::Local &Local) override { - return this->addLocal(Local); - } - -private: - Program::DeclScope Scope; -}; - -/// Scope used to handle initialization methods. -template <class Emitter> class OptionScope { -public: - using InitFnRef = typename ByteCodeExprGen<Emitter>::InitFnRef; - using ChainedInitFnRef = std::function<bool(InitFnRef)>; - - /// Root constructor, compiling or discarding primitives. - OptionScope(ByteCodeExprGen<Emitter> *Ctx, bool NewDiscardResult) - : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), - OldInitFn(std::move(Ctx->InitFn)) { - Ctx->DiscardResult = NewDiscardResult; - Ctx->InitFn = llvm::Optional<InitFnRef>{}; - } - - /// Root constructor, setting up compilation state. - OptionScope(ByteCodeExprGen<Emitter> *Ctx, InitFnRef NewInitFn) - : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), - OldInitFn(std::move(Ctx->InitFn)) { - Ctx->DiscardResult = true; - Ctx->InitFn = NewInitFn; - } - - /// Extends the chain of initialisation pointers. - OptionScope(ByteCodeExprGen<Emitter> *Ctx, ChainedInitFnRef NewInitFn) - : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), - OldInitFn(std::move(Ctx->InitFn)) { - assert(OldInitFn && "missing initializer"); - Ctx->InitFn = [this, NewInitFn] { return NewInitFn(*OldInitFn); }; - } - - ~OptionScope() { - Ctx->DiscardResult = OldDiscardResult; - Ctx->InitFn = std::move(OldInitFn); - } - -private: - /// Parent context. - ByteCodeExprGen<Emitter> *Ctx; - /// Old discard flag to restore. - bool OldDiscardResult; - /// Old pointer emitter to restore. - llvm::Optional<InitFnRef> OldInitFn; -}; - -} // namespace interp -} // namespace clang - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) { - auto *SubExpr = CE->getSubExpr(); - switch (CE->getCastKind()) { - - case CK_LValueToRValue: { - return dereference( - CE->getSubExpr(), DerefKind::Read, - [](PrimType) { - // Value loaded - nothing to do here. - return true; - }, - [this, CE](PrimType T) { - // Pointer on stack - dereference it. - if (!this->emitLoadPop(T, CE)) - return false; - return DiscardResult ? this->emitPop(T, CE) : true; - }); - } - - case CK_ArrayToPointerDecay: - case CK_AtomicToNonAtomic: - case CK_ConstructorConversion: - case CK_FunctionToPointerDecay: - case CK_NonAtomicToAtomic: - case CK_NoOp: - case CK_UserDefinedConversion: - return this->Visit(SubExpr); - - case CK_ToVoid: - return discard(SubExpr); - - default: { - // TODO: implement other casts. - return this->bail(CE); - } - } -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::VisitIntegerLiteral(const IntegerLiteral *LE) { - if (DiscardResult) - return true; - - auto Val = LE->getValue(); - QualType LitTy = LE->getType(); - if (Optional<PrimType> T = classify(LitTy)) - return emitConst(*T, getIntWidth(LitTy), LE->getValue(), LE); - return this->bail(LE); -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::VisitParenExpr(const ParenExpr *PE) { - return this->Visit(PE->getSubExpr()); -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) { - const Expr *LHS = BO->getLHS(); - const Expr *RHS = BO->getRHS(); - - // Deal with operations which have composite or void types. - switch (BO->getOpcode()) { - case BO_Comma: - if (!discard(LHS)) - return false; - if (!this->Visit(RHS)) - return false; - return true; - default: - break; - } - - // Typecheck the args. - Optional<PrimType> LT = classify(LHS->getType()); - Optional<PrimType> RT = classify(RHS->getType()); - if (!LT || !RT) { - return this->bail(BO); - } - - if (Optional<PrimType> T = classify(BO->getType())) { - if (!visit(LHS)) - return false; - if (!visit(RHS)) - return false; - - auto Discard = [this, T, BO](bool Result) { - if (!Result) - return false; - return DiscardResult ? this->emitPop(*T, BO) : true; - }; - - switch (BO->getOpcode()) { - case BO_EQ: - return Discard(this->emitEQ(*LT, BO)); - case BO_NE: - return Discard(this->emitNE(*LT, BO)); - case BO_LT: - return Discard(this->emitLT(*LT, BO)); - case BO_LE: - return Discard(this->emitLE(*LT, BO)); - case BO_GT: - return Discard(this->emitGT(*LT, BO)); - case BO_GE: - return Discard(this->emitGE(*LT, BO)); - case BO_Sub: - return Discard(this->emitSub(*T, BO)); - case BO_Add: - return Discard(this->emitAdd(*T, BO)); - case BO_Mul: - return Discard(this->emitMul(*T, BO)); - default: - return this->bail(BO); - } - } - - return this->bail(BO); -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::discard(const Expr *E) { - OptionScope<Emitter> Scope(this, /*discardResult=*/true); - return this->Visit(E); -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::visit(const Expr *E) { - OptionScope<Emitter> Scope(this, /*discardResult=*/false); - return this->Visit(E); -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::visitBool(const Expr *E) { - if (Optional<PrimType> T = classify(E->getType())) { - return visit(E); - } else { - return this->bail(E); - } -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::visitZeroInitializer(PrimType T, const Expr *E) { - switch (T) { - case PT_Bool: - return this->emitZeroBool(E); - case PT_Sint8: - return this->emitZeroSint8(E); - case PT_Uint8: - return this->emitZeroUint8(E); - case PT_Sint16: - return this->emitZeroSint16(E); - case PT_Uint16: - return this->emitZeroUint16(E); - case PT_Sint32: - return this->emitZeroSint32(E); - case PT_Uint32: - return this->emitZeroUint32(E); - case PT_Sint64: - return this->emitZeroSint64(E); - case PT_Uint64: - return this->emitZeroUint64(E); - case PT_Ptr: - return this->emitNullPtr(E); - } - llvm_unreachable("unknown primitive type"); -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::dereference( - const Expr *LV, DerefKind AK, llvm::function_ref<bool(PrimType)> Direct, - llvm::function_ref<bool(PrimType)> Indirect) { - if (Optional<PrimType> T = classify(LV->getType())) { - if (!LV->refersToBitField()) { - // Only primitive, non bit-field types can be dereferenced directly. - if (auto *DE = dyn_cast<DeclRefExpr>(LV)) { - if (!DE->getDecl()->getType()->isReferenceType()) { - if (auto *PD = dyn_cast<ParmVarDecl>(DE->getDecl())) - return dereferenceParam(LV, *T, PD, AK, Direct, Indirect); - if (auto *VD = dyn_cast<VarDecl>(DE->getDecl())) - return dereferenceVar(LV, *T, VD, AK, Direct, Indirect); - } - } - } - - if (!visit(LV)) - return false; - return Indirect(*T); - } - - return false; -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::dereferenceParam( - const Expr *LV, PrimType T, const ParmVarDecl *PD, DerefKind AK, - llvm::function_ref<bool(PrimType)> Direct, - llvm::function_ref<bool(PrimType)> Indirect) { - auto It = this->Params.find(PD); - if (It != this->Params.end()) { - unsigned Idx = It->second; - switch (AK) { - case DerefKind::Read: - return DiscardResult ? true : this->emitGetParam(T, Idx, LV); - - case DerefKind::Write: - if (!Direct(T)) - return false; - if (!this->emitSetParam(T, Idx, LV)) - return false; - return DiscardResult ? true : this->emitGetPtrParam(Idx, LV); - - case DerefKind::ReadWrite: - if (!this->emitGetParam(T, Idx, LV)) - return false; - if (!Direct(T)) - return false; - if (!this->emitSetParam(T, Idx, LV)) - return false; - return DiscardResult ? true : this->emitGetPtrParam(Idx, LV); - } - return true; - } - - // If the param is a pointer, we can dereference a dummy value. - if (!DiscardResult && T == PT_Ptr && AK == DerefKind::Read) { - if (auto Idx = P.getOrCreateDummy(PD)) - return this->emitGetPtrGlobal(*Idx, PD); - return false; - } - - // Value cannot be produced - try to emit pointer and do stuff with it. - return visit(LV) && Indirect(T); -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::dereferenceVar( - const Expr *LV, PrimType T, const VarDecl *VD, DerefKind AK, - llvm::function_ref<bool(PrimType)> Direct, - llvm::function_ref<bool(PrimType)> Indirect) { - auto It = Locals.find(VD); - if (It != Locals.end()) { - const auto &L = It->second; - switch (AK) { - case DerefKind::Read: - if (!this->emitGetLocal(T, L.Offset, LV)) - return false; - return DiscardResult ? this->emitPop(T, LV) : true; - - case DerefKind::Write: - if (!Direct(T)) - return false; - if (!this->emitSetLocal(T, L.Offset, LV)) - return false; - return DiscardResult ? true : this->emitGetPtrLocal(L.Offset, LV); - - case DerefKind::ReadWrite: - if (!this->emitGetLocal(T, L.Offset, LV)) - return false; - if (!Direct(T)) - return false; - if (!this->emitSetLocal(T, L.Offset, LV)) - return false; - return DiscardResult ? true : this->emitGetPtrLocal(L.Offset, LV); - } - } else if (auto Idx = getGlobalIdx(VD)) { - switch (AK) { - case DerefKind::Read: - if (!this->emitGetGlobal(T, *Idx, LV)) - return false; - return DiscardResult ? this->emitPop(T, LV) : true; - - case DerefKind::Write: - if (!Direct(T)) - return false; - if (!this->emitSetGlobal(T, *Idx, LV)) - return false; - return DiscardResult ? true : this->emitGetPtrGlobal(*Idx, LV); - - case DerefKind::ReadWrite: - if (!this->emitGetGlobal(T, *Idx, LV)) - return false; - if (!Direct(T)) - return false; - if (!this->emitSetGlobal(T, *Idx, LV)) - return false; - return DiscardResult ? true : this->emitGetPtrGlobal(*Idx, LV); - } - } - - // If the declaration is a constant value, emit it here even - // though the declaration was not evaluated in the current scope. - // The access mode can only be read in this case. - if (!DiscardResult && AK == DerefKind::Read) { - if (VD->hasLocalStorage() && VD->hasInit() && !VD->isConstexpr()) { - QualType VT = VD->getType(); - if (VT.isConstQualified() && VT->isFundamentalType()) - return this->Visit(VD->getInit()); - } - } - - // Value cannot be produced - try to emit pointer. - return visit(LV) && Indirect(T); -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::emitConst(PrimType T, unsigned NumBits, - const APInt &Value, const Expr *E) { - switch (T) { - case PT_Sint8: - return this->emitConstSint8(Value.getSExtValue(), E); - case PT_Uint8: - return this->emitConstUint8(Value.getZExtValue(), E); - case PT_Sint16: - return this->emitConstSint16(Value.getSExtValue(), E); - case PT_Uint16: - return this->emitConstUint16(Value.getZExtValue(), E); - case PT_Sint32: - return this->emitConstSint32(Value.getSExtValue(), E); - case PT_Uint32: - return this->emitConstUint32(Value.getZExtValue(), E); - case PT_Sint64: - return this->emitConstSint64(Value.getSExtValue(), E); - case PT_Uint64: - return this->emitConstUint64(Value.getZExtValue(), E); - case PT_Bool: - return this->emitConstBool(Value.getBoolValue(), E); - case PT_Ptr: - llvm_unreachable("Invalid integral type"); - break; - } - llvm_unreachable("unknown primitive type"); -} - -template <class Emitter> -unsigned ByteCodeExprGen<Emitter>::allocateLocalPrimitive(DeclTy &&Src, - PrimType Ty, - bool IsConst, - bool IsExtended) { - Descriptor *D = P.createDescriptor(Src, Ty, IsConst, Src.is<const Expr *>()); - Scope::Local Local = this->createLocal(D); - if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>())) - Locals.insert({VD, Local}); - VarScope->add(Local, IsExtended); - return Local.Offset; -} - -template <class Emitter> -llvm::Optional<unsigned> -ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) { - QualType Ty; - - const ValueDecl *Key = nullptr; - bool IsTemporary = false; - if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>())) { - Key = VD; - Ty = VD->getType(); - } - if (auto *E = Src.dyn_cast<const Expr *>()) { - IsTemporary = true; - Ty = E->getType(); - } - - Descriptor *D = P.createDescriptor(Src, Ty.getTypePtr(), - Ty.isConstQualified(), IsTemporary); - if (!D) - return {}; - - Scope::Local Local = this->createLocal(D); - if (Key) - Locals.insert({Key, Local}); - VarScope->add(Local, IsExtended); - return Local.Offset; -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::visitInitializer( - const Expr *Init, InitFnRef InitFn) { - OptionScope<Emitter> Scope(this, InitFn); - return this->Visit(Init); -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::getPtrVarDecl(const VarDecl *VD, const Expr *E) { - // Generate a pointer to the local, loading refs. - if (Optional<unsigned> Idx = getGlobalIdx(VD)) { - if (VD->getType()->isReferenceType()) - return this->emitGetGlobalPtr(*Idx, E); - else - return this->emitGetPtrGlobal(*Idx, E); - } - return this->bail(VD); -} - -template <class Emitter> -llvm::Optional<unsigned> -ByteCodeExprGen<Emitter>::getGlobalIdx(const VarDecl *VD) { - if (VD->isConstexpr()) { - // Constexpr decl - it must have already been defined. - return P.getGlobal(VD); - } - if (!VD->hasLocalStorage()) { - // Not constexpr, but a global var - can have pointer taken. - Program::DeclScope Scope(P, VD); - return P.getOrCreateGlobal(VD); - } - return {}; -} - -template <class Emitter> -const RecordType *ByteCodeExprGen<Emitter>::getRecordTy(QualType Ty) { - if (auto *PT = dyn_cast<PointerType>(Ty)) - return PT->getPointeeType()->getAs<RecordType>(); - else - return Ty->getAs<RecordType>(); -} - -template <class Emitter> -Record *ByteCodeExprGen<Emitter>::getRecord(QualType Ty) { - if (auto *RecordTy = getRecordTy(Ty)) { - return getRecord(RecordTy->getDecl()); - } - return nullptr; -} - -template <class Emitter> -Record *ByteCodeExprGen<Emitter>::getRecord(const RecordDecl *RD) { - return P.getOrCreateRecord(RD); -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::visitExpr(const Expr *Exp) { - ExprScope<Emitter> RootScope(this); - if (!visit(Exp)) - return false; - - if (Optional<PrimType> T = classify(Exp)) - return this->emitRet(*T, Exp); - else - return this->emitRetValue(Exp); -} - -template <class Emitter> -bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD) { - const Expr *Init = VD->getInit(); - - if (Optional<unsigned> I = P.createGlobal(VD)) { - if (Optional<PrimType> T = classify(VD->getType())) { - { - // Primitive declarations - compute the value and set it. - DeclScope<Emitter> LocalScope(this, VD); - if (!visit(Init)) - return false; - } - - // If the declaration is global, save the value for later use. - if (!this->emitDup(*T, VD)) - return false; - if (!this->emitInitGlobal(*T, *I, VD)) - return false; - return this->emitRet(*T, VD); - } else { - { - // Composite declarations - allocate storage and initialize it. - DeclScope<Emitter> LocalScope(this, VD); - if (!visitGlobalInitializer(Init, *I)) - return false; - } - - // Return a pointer to the global. - if (!this->emitGetPtrGlobal(*I, VD)) - return false; - return this->emitRetValue(VD); - } - } - - return this->bail(VD); -} - -template <class Emitter> -void ByteCodeExprGen<Emitter>::emitCleanup() { - for (VariableScope<Emitter> *C = VarScope; C; C = C->getParent()) - C->emitDestruction(); -} - -namespace clang { -namespace interp { - -template class ByteCodeExprGen<ByteCodeEmitter>; -template class ByteCodeExprGen<EvalEmitter>; - -} // namespace interp -} // namespace clang diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h deleted file mode 100644 index f0ec1203292..00000000000 --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ /dev/null @@ -1,340 +0,0 @@ -//===--- ByteCodeExprGen.h - Code generator for expressions -----*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the constexpr bytecode compiler. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_BYTECODEEXPRGEN_H -#define LLVM_CLANG_AST_INTERP_BYTECODEEXPRGEN_H - -#include "ByteCodeEmitter.h" -#include "EvalEmitter.h" -#include "Pointer.h" -#include "Record.h" -#include "Type.h" -#include "clang/AST/Decl.h" -#include "clang/AST/Expr.h" -#include "clang/AST/StmtVisitor.h" -#include "llvm/ADT/Optional.h" - -namespace clang { -class QualType; - -namespace interp { -class Function; -class State; - -template <class Emitter> class LocalScope; -template <class Emitter> class RecordScope; -template <class Emitter> class VariableScope; -template <class Emitter> class DeclScope; -template <class Emitter> class OptionScope; - -/// Compilation context for expressions. -template <class Emitter> -class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>, - public Emitter { -protected: - // Emitters for opcodes of various arities. - using NullaryFn = bool (ByteCodeExprGen::*)(const SourceInfo &); - using UnaryFn = bool (ByteCodeExprGen::*)(PrimType, const SourceInfo &); - using BinaryFn = bool (ByteCodeExprGen::*)(PrimType, PrimType, - const SourceInfo &); - - // Aliases for types defined in the emitter. - using LabelTy = typename Emitter::LabelTy; - using AddrTy = typename Emitter::AddrTy; - - // Reference to a function generating the pointer of an initialized object.s - using InitFnRef = std::function<bool()>; - - /// Current compilation context. - Context &Ctx; - /// Program to link to. - Program &P; - -public: - /// Initializes the compiler and the backend emitter. - template <typename... Tys> - ByteCodeExprGen(Context &Ctx, Program &P, Tys &&... Args) - : Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {} - - // Expression visitors - result returned on stack. - bool VisitCastExpr(const CastExpr *E); - bool VisitIntegerLiteral(const IntegerLiteral *E); - bool VisitParenExpr(const ParenExpr *E); - bool VisitBinaryOperator(const BinaryOperator *E); - -protected: - bool visitExpr(const Expr *E) override; - bool visitDecl(const VarDecl *VD) override; - -protected: - /// Emits scope cleanup instructions. - void emitCleanup(); - - /// Returns a record type from a record or pointer type. - const RecordType *getRecordTy(QualType Ty); - - /// Returns a record from a record or pointer type. - Record *getRecord(QualType Ty); - Record *getRecord(const RecordDecl *RD); - - /// Returns the size int bits of an integer. - unsigned getIntWidth(QualType Ty) { - auto &ASTContext = Ctx.getASTContext(); - return ASTContext.getIntWidth(Ty); - } - - /// Returns the value of CHAR_BIT. - unsigned getCharBit() const { - auto &ASTContext = Ctx.getASTContext(); - return ASTContext.getTargetInfo().getCharWidth(); - } - - /// Classifies a type. - llvm::Optional<PrimType> classify(const Expr *E) const { - return E->isGLValue() ? PT_Ptr : classify(E->getType()); - } - llvm::Optional<PrimType> classify(QualType Ty) const { - return Ctx.classify(Ty); - } - - /// Checks if a pointer needs adjustment. - bool needsAdjust(QualType Ty) const { - return true; - } - - /// Classifies a known primitive type - PrimType classifyPrim(QualType Ty) const { - if (auto T = classify(Ty)) { - return *T; - } - llvm_unreachable("not a primitive type"); - } - - /// Evaluates an expression for side effects and discards the result. - bool discard(const Expr *E); - /// Evaluates an expression and places result on stack. - bool visit(const Expr *E); - /// Compiles an initializer for a local. - bool visitInitializer(const Expr *E, InitFnRef GenPtr); - - /// Visits an expression and converts it to a boolean. - bool visitBool(const Expr *E); - - /// Visits an initializer for a local. - bool visitLocalInitializer(const Expr *Init, unsigned I) { - return visitInitializer(Init, [this, I, Init] { - return this->emitGetPtrLocal(I, Init); - }); - } - - /// Visits an initializer for a global. - bool visitGlobalInitializer(const Expr *Init, unsigned I) { - return visitInitializer(Init, [this, I, Init] { - return this->emitGetPtrGlobal(I, Init); - }); - } - - /// Visits a delegated initializer. - bool visitThisInitializer(const Expr *I) { - return visitInitializer(I, [this, I] { return this->emitThis(I); }); - } - - /// Creates a local primitive value. - unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsMutable, - bool IsExtended = false); - - /// Allocates a space storing a local given its type. - llvm::Optional<unsigned> allocateLocal(DeclTy &&Decl, - bool IsExtended = false); - -private: - friend class VariableScope<Emitter>; - friend class LocalScope<Emitter>; - friend class RecordScope<Emitter>; - friend class DeclScope<Emitter>; - friend class OptionScope<Emitter>; - - /// Emits a zero initializer. - bool visitZeroInitializer(PrimType T, const Expr *E); - - /// Fetches a member of a structure given by a pointer. - bool visitIndirectMember(const BinaryOperator *E); - - /// Emits a cast between two types. - bool emitConv(PrimType From, QualType FromTy, PrimType To, QualType ToTy, - const Expr *Cast); - - enum class DerefKind { - /// Value is read and pushed to stack. - Read, - /// Direct method generates a value which is written. Returns pointer. - Write, - /// Direct method receives the value, pushes mutated value. Returns pointer. - ReadWrite, - }; - - /// Method to directly load a value. If the value can be fetched directly, - /// the direct handler is called. Otherwise, a pointer is left on the stack - /// and the indirect handler is expected to operate on that. - bool dereference(const Expr *LV, DerefKind AK, - llvm::function_ref<bool(PrimType)> Direct, - llvm::function_ref<bool(PrimType)> Indirect); - bool dereferenceParam(const Expr *LV, PrimType T, const ParmVarDecl *PD, - DerefKind AK, - llvm::function_ref<bool(PrimType)> Direct, - llvm::function_ref<bool(PrimType)> Indirect); - bool dereferenceVar(const Expr *LV, PrimType T, const VarDecl *PD, - DerefKind AK, llvm::function_ref<bool(PrimType)> Direct, - llvm::function_ref<bool(PrimType)> Indirect); - - /// Emits an APInt constant. - bool emitConst(PrimType T, unsigned NumBits, const llvm::APInt &Value, - const Expr *E); - - /// Emits an integer constant. - template <typename T> bool emitConst(const Expr *E, T Value) { - QualType Ty = E->getType(); - unsigned NumBits = getIntWidth(Ty); - APInt WrappedValue(NumBits, Value, std::is_signed<T>::value); - return emitConst(*Ctx.classify(Ty), NumBits, WrappedValue, E); - } - - /// Visits a constant function invocation. - bool getPtrConstFn(const FunctionDecl *FD, const Expr *E); - /// Returns a pointer to a variable declaration. - bool getPtrVarDecl(const VarDecl *VD, const Expr *E); - - /// Returns the index of a global. - llvm::Optional<unsigned> getGlobalIdx(const VarDecl *VD); - - /// Emits the initialized pointer. - bool emitInitFn() { - assert(InitFn && "missing initializer"); - return (*InitFn)(); - } - -protected: - /// Variable to storage mapping. - llvm::DenseMap<const ValueDecl *, Scope::Local> Locals; - - /// OpaqueValueExpr to location mapping. - llvm::DenseMap<const OpaqueValueExpr *, unsigned> OpaqueExprs; - - /// Current scope. - VariableScope<Emitter> *VarScope = nullptr; - - /// Current argument index. - llvm::Optional<uint64_t> ArrayIndex; - - /// Flag indicating if return value is to be discarded. - bool DiscardResult = false; - - /// Expression being initialized. - llvm::Optional<InitFnRef> InitFn = {}; -}; - -extern template class ByteCodeExprGen<ByteCodeEmitter>; -extern template class ByteCodeExprGen<EvalEmitter>; - -/// Scope chain managing the variable lifetimes. -template <class Emitter> class VariableScope { -public: - virtual ~VariableScope() { Ctx->VarScope = this->Parent; } - - void add(const Scope::Local &Local, bool IsExtended) { - if (IsExtended) - this->addExtended(Local); - else - this->addLocal(Local); - } - - virtual void addLocal(const Scope::Local &Local) { - if (this->Parent) - this->Parent->addLocal(Local); - } - - virtual void addExtended(const Scope::Local &Local) { - if (this->Parent) - this->Parent->addExtended(Local); - } - - virtual void emitDestruction() {} - - VariableScope *getParent() { return Parent; } - -protected: - VariableScope(ByteCodeExprGen<Emitter> *Ctx) - : Ctx(Ctx), Parent(Ctx->VarScope) { - Ctx->VarScope = this; - } - - /// ByteCodeExprGen instance. - ByteCodeExprGen<Emitter> *Ctx; - /// Link to the parent scope. - VariableScope *Parent; -}; - -/// Scope for local variables. -/// -/// When the scope is destroyed, instructions are emitted to tear down -/// all variables declared in this scope. -template <class Emitter> class LocalScope : public VariableScope<Emitter> { -public: - LocalScope(ByteCodeExprGen<Emitter> *Ctx) : VariableScope<Emitter>(Ctx) {} - - ~LocalScope() override { this->emitDestruction(); } - - void addLocal(const Scope::Local &Local) override { - if (!Idx.hasValue()) { - Idx = this->Ctx->Descriptors.size(); - this->Ctx->Descriptors.emplace_back(); - } - - this->Ctx->Descriptors[*Idx].emplace_back(Local); - } - - void emitDestruction() override { - if (!Idx.hasValue()) - return; - this->Ctx->emitDestroy(*Idx, SourceInfo{}); - } - -protected: - /// Index of the scope in the chain. - Optional<unsigned> Idx; -}; - -/// Scope for storage declared in a compound statement. -template <class Emitter> class BlockScope final : public LocalScope<Emitter> { -public: - BlockScope(ByteCodeExprGen<Emitter> *Ctx) : LocalScope<Emitter>(Ctx) {} - - void addExtended(const Scope::Local &Local) override { - llvm_unreachable("Cannot create temporaries in full scopes"); - } -}; - -/// Expression scope which tracks potentially lifetime extended -/// temporaries which are hoisted to the parent scope on exit. -template <class Emitter> class ExprScope final : public LocalScope<Emitter> { -public: - ExprScope(ByteCodeExprGen<Emitter> *Ctx) : LocalScope<Emitter>(Ctx) {} - - void addExtended(const Scope::Local &Local) override { - this->Parent->addLocal(Local); - } -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/ByteCodeGenError.cpp b/clang/lib/AST/Interp/ByteCodeGenError.cpp deleted file mode 100644 index 5fd3d77c384..00000000000 --- a/clang/lib/AST/Interp/ByteCodeGenError.cpp +++ /dev/null @@ -1,14 +0,0 @@ -//===--- ByteCodeGenError.h - Byte code generation error --------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "ByteCodeGenError.h" - -using namespace clang; -using namespace clang::interp; - -char ByteCodeGenError::ID; diff --git a/clang/lib/AST/Interp/ByteCodeGenError.h b/clang/lib/AST/Interp/ByteCodeGenError.h deleted file mode 100644 index a4fa4917705..00000000000 --- a/clang/lib/AST/Interp/ByteCodeGenError.h +++ /dev/null @@ -1,46 +0,0 @@ -//===--- ByteCodeGenError.h - Byte code generation error ----------*- C -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_BYTECODEGENERROR_H -#define LLVM_CLANG_AST_INTERP_BYTECODEGENERROR_H - -#include "clang/AST/Decl.h" -#include "clang/AST/Stmt.h" -#include "clang/Basic/SourceLocation.h" -#include "llvm/Support/Error.h" - -namespace clang { -namespace interp { - -/// Error thrown by the compiler. -struct ByteCodeGenError : public llvm::ErrorInfo<ByteCodeGenError> { -public: - ByteCodeGenError(SourceLocation Loc) : Loc(Loc) {} - ByteCodeGenError(const Stmt *S) : ByteCodeGenError(S->getBeginLoc()) {} - ByteCodeGenError(const Decl *D) : ByteCodeGenError(D->getBeginLoc()) {} - - void log(raw_ostream &OS) const override { OS << "unimplemented feature"; } - - const SourceLocation &getLoc() const { return Loc; } - - static char ID; - -private: - // Start of the item where the error occurred. - SourceLocation Loc; - - // Users are not expected to use error_code. - std::error_code convertToErrorCode() const override { - return llvm::inconvertibleErrorCode(); - } -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp deleted file mode 100644 index 25d9cadf6d3..00000000000 --- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ /dev/null @@ -1,265 +0,0 @@ -//===--- ByteCodeStmtGen.cpp - Code generator for expressions ---*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "ByteCodeStmtGen.h" -#include "ByteCodeEmitter.h" -#include "ByteCodeGenError.h" -#include "Context.h" -#include "Function.h" -#include "Program.h" -#include "State.h" -#include "Type.h" - -using namespace clang; -using namespace clang::interp; - -template <typename T> using Expected = llvm::Expected<T>; -template <typename T> using Optional = llvm::Optional<T>; - -namespace clang { -namespace interp { - -/// Scope managing label targets. -template <class Emitter> class LabelScope { -public: - virtual ~LabelScope() { } - -protected: - LabelScope(ByteCodeStmtGen<Emitter> *Ctx) : Ctx(Ctx) {} - /// ByteCodeStmtGen instance. - ByteCodeStmtGen<Emitter> *Ctx; -}; - -/// Sets the context for break/continue statements. -template <class Emitter> class LoopScope final : public LabelScope<Emitter> { -public: - using LabelTy = typename ByteCodeStmtGen<Emitter>::LabelTy; - using OptLabelTy = typename ByteCodeStmtGen<Emitter>::OptLabelTy; - - LoopScope(ByteCodeStmtGen<Emitter> *Ctx, LabelTy BreakLabel, - LabelTy ContinueLabel) - : LabelScope<Emitter>(Ctx), OldBreakLabel(Ctx->BreakLabel), - OldContinueLabel(Ctx->ContinueLabel) { - this->Ctx->BreakLabel = BreakLabel; - this->Ctx->ContinueLabel = ContinueLabel; - } - - ~LoopScope() { - this->Ctx->BreakLabel = OldBreakLabel; - this->Ctx->ContinueLabel = OldContinueLabel; - } - -private: - OptLabelTy OldBreakLabel; - OptLabelTy OldContinueLabel; -}; - -// Sets the context for a switch scope, mapping labels. -template <class Emitter> class SwitchScope final : public LabelScope<Emitter> { -public: - using LabelTy = typename ByteCodeStmtGen<Emitter>::LabelTy; - using OptLabelTy = typename ByteCodeStmtGen<Emitter>::OptLabelTy; - using CaseMap = typename ByteCodeStmtGen<Emitter>::CaseMap; - - SwitchScope(ByteCodeStmtGen<Emitter> *Ctx, CaseMap &&CaseLabels, - LabelTy BreakLabel, OptLabelTy DefaultLabel) - : LabelScope<Emitter>(Ctx), OldBreakLabel(Ctx->BreakLabel), - OldDefaultLabel(this->Ctx->DefaultLabel), - OldCaseLabels(std::move(this->Ctx->CaseLabels)) { - this->Ctx->BreakLabel = BreakLabel; - this->Ctx->DefaultLabel = DefaultLabel; - this->Ctx->CaseLabels = std::move(CaseLabels); - } - - ~SwitchScope() { - this->Ctx->BreakLabel = OldBreakLabel; - this->Ctx->DefaultLabel = OldDefaultLabel; - this->Ctx->CaseLabels = std::move(OldCaseLabels); - } - -private: - OptLabelTy OldBreakLabel; - OptLabelTy OldDefaultLabel; - CaseMap OldCaseLabels; -}; - -} // namespace interp -} // namespace clang - -template <class Emitter> -bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) { - // Classify the return type. - ReturnType = this->classify(F->getReturnType()); - - // Set up fields and context if a constructor. - if (auto *MD = dyn_cast<CXXMethodDecl>(F)) - return this->bail(MD); - - if (auto *Body = F->getBody()) - if (!visitStmt(Body)) - return false; - - // Emit a guard return to protect against a code path missing one. - if (F->getReturnType()->isVoidType()) - return this->emitRetVoid(SourceInfo{}); - else - return this->emitNoRet(SourceInfo{}); -} - -template <class Emitter> -bool ByteCodeStmtGen<Emitter>::visitStmt(const Stmt *S) { - switch (S->getStmtClass()) { - case Stmt::CompoundStmtClass: - return visitCompoundStmt(cast<CompoundStmt>(S)); - case Stmt::DeclStmtClass: - return visitDeclStmt(cast<DeclStmt>(S)); - case Stmt::ReturnStmtClass: - return visitReturnStmt(cast<ReturnStmt>(S)); - case Stmt::IfStmtClass: - return visitIfStmt(cast<IfStmt>(S)); - case Stmt::NullStmtClass: - return true; - default: { - if (auto *Exp = dyn_cast<Expr>(S)) - return this->discard(Exp); - return this->bail(S); - } - } -} - -template <class Emitter> -bool ByteCodeStmtGen<Emitter>::visitCompoundStmt( - const CompoundStmt *CompoundStmt) { - BlockScope<Emitter> Scope(this); - for (auto *InnerStmt : CompoundStmt->body()) - if (!visitStmt(InnerStmt)) - return false; - return true; -} - -template <class Emitter> -bool ByteCodeStmtGen<Emitter>::visitDeclStmt(const DeclStmt *DS) { - for (auto *D : DS->decls()) { - // Variable declarator. - if (auto *VD = dyn_cast<VarDecl>(D)) { - if (!visitVarDecl(VD)) - return false; - continue; - } - - // Decomposition declarator. - if (auto *DD = dyn_cast<DecompositionDecl>(D)) { - return this->bail(DD); - } - } - - return true; -} - -template <class Emitter> -bool ByteCodeStmtGen<Emitter>::visitReturnStmt(const ReturnStmt *RS) { - if (const Expr *RE = RS->getRetValue()) { - ExprScope<Emitter> RetScope(this); - if (ReturnType) { - // Primitive types are simply returned. - if (!this->visit(RE)) - return false; - this->emitCleanup(); - return this->emitRet(*ReturnType, RS); - } else { - // RVO - construct the value in the return location. - auto ReturnLocation = [this, RE] { return this->emitGetParamPtr(0, RE); }; - if (!this->visitInitializer(RE, ReturnLocation)) - return false; - this->emitCleanup(); - return this->emitRetVoid(RS); - } - } else { - this->emitCleanup(); - if (!this->emitRetVoid(RS)) - return false; - return true; - } -} - -template <class Emitter> -bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) { - BlockScope<Emitter> IfScope(this); - if (auto *CondInit = IS->getInit()) - if (!visitStmt(IS->getInit())) - return false; - - if (const DeclStmt *CondDecl = IS->getConditionVariableDeclStmt()) - if (!visitDeclStmt(CondDecl)) - return false; - - if (!this->visitBool(IS->getCond())) - return false; - - if (const Stmt *Else = IS->getElse()) { - LabelTy LabelElse = this->getLabel(); - LabelTy LabelEnd = this->getLabel(); - if (!this->jumpFalse(LabelElse)) - return false; - if (!visitStmt(IS->getThen())) - return false; - if (!this->jump(LabelEnd)) - return false; - this->emitLabel(LabelElse); - if (!visitStmt(Else)) - return false; - this->emitLabel(LabelEnd); - } else { - LabelTy LabelEnd = this->getLabel(); - if (!this->jumpFalse(LabelEnd)) - return false; - if (!visitStmt(IS->getThen())) - return false; - this->emitLabel(LabelEnd); - } - - return true; -} - -template <class Emitter> -bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) { - auto DT = VD->getType(); - - if (!VD->hasLocalStorage()) { - // No code generation required. - return true; - } - - // Integers, pointers, primitives. - if (Optional<PrimType> T = this->classify(DT)) { - auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified()); - // Compile the initialiser in its own scope. - { - ExprScope<Emitter> Scope(this); - if (!this->visit(VD->getInit())) - return false; - } - // Set the value. - return this->emitSetLocal(*T, Off, VD); - } else { - // Composite types - allocate storage and initialize it. - if (auto Off = this->allocateLocal(VD)) { - return this->visitLocalInitializer(VD->getInit(), *Off); - } else { - return this->bail(VD); - } - } -} - -namespace clang { -namespace interp { - -template class ByteCodeStmtGen<ByteCodeEmitter>; - -} // namespace interp -} // namespace clang diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.h b/clang/lib/AST/Interp/ByteCodeStmtGen.h deleted file mode 100644 index 744ebc64726..00000000000 --- a/clang/lib/AST/Interp/ByteCodeStmtGen.h +++ /dev/null @@ -1,89 +0,0 @@ -//===--- ByteCodeStmtGen.h - Code generator for expressions -----*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the constexpr bytecode compiler. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_BYTECODESTMTGEN_H -#define LLVM_CLANG_AST_INTERP_BYTECODESTMTGEN_H - -#include "ByteCodeExprGen.h" -#include "ByteCodeEmitter.h" -#include "EvalEmitter.h" -#include "Pointer.h" -#include "Record.h" -#include "Type.h" -#include "clang/AST/Decl.h" -#include "clang/AST/Expr.h" -#include "clang/AST/StmtVisitor.h" -#include "llvm/ADT/Optional.h" - -namespace clang { -class QualType; - -namespace interp { -class Function; -class State; - -template <class Emitter> class LoopScope; -template <class Emitter> class SwitchScope; -template <class Emitter> class LabelScope; - -/// Compilation context for statements. -template <class Emitter> -class ByteCodeStmtGen : public ByteCodeExprGen<Emitter> { - using LabelTy = typename Emitter::LabelTy; - using AddrTy = typename Emitter::AddrTy; - using OptLabelTy = llvm::Optional<LabelTy>; - using CaseMap = llvm::DenseMap<const SwitchCase *, LabelTy>; - -public: - template<typename... Tys> - ByteCodeStmtGen(Tys&&... Args) - : ByteCodeExprGen<Emitter>(std::forward<Tys>(Args)...) {} - -protected: - bool visitFunc(const FunctionDecl *F) override; - -private: - friend class LabelScope<Emitter>; - friend class LoopScope<Emitter>; - friend class SwitchScope<Emitter>; - - // Statement visitors. - bool visitStmt(const Stmt *S); - bool visitCompoundStmt(const CompoundStmt *S); - bool visitDeclStmt(const DeclStmt *DS); - bool visitReturnStmt(const ReturnStmt *RS); - bool visitIfStmt(const IfStmt *IS); - - /// Compiles a variable declaration. - bool visitVarDecl(const VarDecl *VD); - -private: - /// Type of the expression returned by the function. - llvm::Optional<PrimType> ReturnType; - - /// Switch case mapping. - CaseMap CaseLabels; - - /// Point to break to. - OptLabelTy BreakLabel; - /// Point to continue to. - OptLabelTy ContinueLabel; - /// Default case label. - OptLabelTy DefaultLabel; -}; - -extern template class ByteCodeExprGen<EvalEmitter>; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/CMakeLists.txt b/clang/lib/AST/Interp/CMakeLists.txt deleted file mode 100644 index 31d7c3cecf7..00000000000 --- a/clang/lib/AST/Interp/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -set(LLVM_LINK_COMPONENTS - ) - -clang_tablegen(Opcodes.inc - -gen-clang-opcodes - SOURCE Opcodes.td - TARGET Opcodes) - -add_clang_library(clangInterp - Block.cpp - ByteCodeEmitter.cpp - ByteCodeExprGen.cpp - ByteCodeStmtGen.cpp - ByteCodeGenError.cpp - Context.cpp - Descriptor.cpp - Disasm.cpp - EvalEmitter.cpp - Frame.cpp - Function.cpp - Interp.cpp - InterpFrame.cpp - InterpStack.cpp - InterpState.cpp - Pointer.cpp - Program.cpp - Record.cpp - Source.cpp - State.cpp - Type.cpp - - DEPENDS - Opcodes - ) diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp deleted file mode 100644 index 85d8626c382..00000000000 --- a/clang/lib/AST/Interp/Context.cpp +++ /dev/null @@ -1,148 +0,0 @@ -//===--- Context.cpp - Context for the constexpr VM -------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Context.h" -#include "ByteCodeExprGen.h" -#include "ByteCodeStmtGen.h" -#include "EvalEmitter.h" -#include "Interp.h" -#include "InterpFrame.h" -#include "InterpStack.h" -#include "ByteCodeEmitter.h" -#include "Program.h" -#include "Type.h" -#include "clang/AST/Expr.h" - -using namespace clang; -using namespace clang::interp; - -Context::Context(ASTContext &Ctx) - : Ctx(Ctx), ForceInterp(getLangOpts().ForceNewConstInterp), - P(new Program(*this)) {} - -Context::~Context() {} - -InterpResult Context::isPotentialConstantExpr(State &Parent, - const FunctionDecl *FD) { - Function *Func = P->getFunction(FD); - if (!Func) { - if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD)) { - Func = *R; - } else if (ForceInterp) { - handleAllErrors(R.takeError(), [&Parent](ByteCodeGenError &Err) { - Parent.FFDiag(Err.getLoc(), diag::err_experimental_clang_interp_failed); - }); - return InterpResult::Fail; - } else { - consumeError(R.takeError()); - return InterpResult::Bail; - } - } - - if (!Func->isConstexpr()) - return InterpResult::Fail; - - APValue Dummy; - return Run(Parent, Func, Dummy); -} - -InterpResult Context::evaluateAsRValue(State &Parent, const Expr *E, - APValue &Result) { - ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result); - return Check(Parent, C.interpretExpr(E)); -} - -InterpResult Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, - APValue &Result) { - ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result); - return Check(Parent, C.interpretDecl(VD)); -} - -const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); } - -llvm::Optional<PrimType> Context::classify(QualType T) { - if (T->isReferenceType() || T->isPointerType()) { - return PT_Ptr; - } - - if (T->isBooleanType()) - return PT_Bool; - - if (T->isSignedIntegerOrEnumerationType()) { - switch (Ctx.getIntWidth(T)) { - case 64: - return PT_Sint64; - case 32: - return PT_Sint32; - case 16: - return PT_Sint16; - case 8: - return PT_Sint8; - default: - return {}; - } - } - - if (T->isUnsignedIntegerOrEnumerationType()) { - switch (Ctx.getIntWidth(T)) { - case 64: - return PT_Uint64; - case 32: - return PT_Uint32; - case 16: - return PT_Uint16; - case 8: - return PT_Uint8; - default: - return {}; - } - } - - if (T->isNullPtrType()) - return PT_Ptr; - - if (auto *AT = dyn_cast<AtomicType>(T)) - return classify(AT->getValueType()); - - return {}; -} - -unsigned Context::getCharBit() const { - return Ctx.getTargetInfo().getCharWidth(); -} - -InterpResult Context::Run(State &Parent, Function *Func, APValue &Result) { - InterpResult Flag; - { - InterpState State(Parent, *P, Stk, *this); - State.Current = new InterpFrame(State, Func, nullptr, {}, {}); - if (Interpret(State, Result)) { - Flag = InterpResult::Success; - } else { - Flag = InterpResult::Fail; - } - } - - if (Flag != InterpResult::Success) - Stk.clear(); - return Flag; -} - -InterpResult Context::Check(State &Parent, llvm::Expected<bool> &&R) { - if (R) { - return *R ? InterpResult::Success : InterpResult::Fail; - } else if (ForceInterp) { - handleAllErrors(R.takeError(), [&Parent](ByteCodeGenError &Err) { - Parent.FFDiag(Err.getLoc(), diag::err_experimental_clang_interp_failed); - }); - return InterpResult::Fail; - } else { - consumeError(R.takeError()); - return InterpResult::Bail; - } -} diff --git a/clang/lib/AST/Interp/Context.h b/clang/lib/AST/Interp/Context.h deleted file mode 100644 index 96368b6e5f0..00000000000 --- a/clang/lib/AST/Interp/Context.h +++ /dev/null @@ -1,100 +0,0 @@ -//===--- Context.h - Context for the constexpr VM ---------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the constexpr execution context. -// -// The execution context manages cached bytecode and the global context. -// It invokes the compiler and interpreter, propagating errors. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_CONTEXT_H -#define LLVM_CLANG_AST_INTERP_CONTEXT_H - -#include "Context.h" -#include "InterpStack.h" -#include "clang/AST/APValue.h" -#include "llvm/ADT/PointerIntPair.h" - -namespace clang { -class ASTContext; -class LangOptions; -class Stmt; -class FunctionDecl; -class VarDecl; - -namespace interp { -class Function; -class Program; -class State; -enum PrimType : unsigned; - -/// Wrapper around interpreter termination results. -enum class InterpResult { - /// Interpreter successfully computed a value. - Success, - /// Interpreter encountered an error and quit. - Fail, - /// Interpreter encountered an unimplemented feature, AST fallback. - Bail, -}; - -/// Holds all information required to evaluate constexpr code in a module. -class Context { -public: - /// Initialises the constexpr VM. - Context(ASTContext &Ctx); - - /// Cleans up the constexpr VM. - ~Context(); - - /// Checks if a function is a potential constant expression. - InterpResult isPotentialConstantExpr(State &Parent, - const FunctionDecl *FnDecl); - - /// Evaluates a toplevel expression as an rvalue. - InterpResult evaluateAsRValue(State &Parent, const Expr *E, APValue &Result); - - /// Evaluates a toplevel initializer. - InterpResult evaluateAsInitializer(State &Parent, const VarDecl *VD, - APValue &Result); - - /// Returns the AST context. - ASTContext &getASTContext() const { return Ctx; } - /// Returns the language options. - const LangOptions &getLangOpts() const; - /// Returns the interpreter stack. - InterpStack &getStack() { return Stk; } - /// Returns CHAR_BIT. - unsigned getCharBit() const; - - /// Classifies an expression. - llvm::Optional<PrimType> classify(QualType T); - -private: - /// Runs a function. - InterpResult Run(State &Parent, Function *Func, APValue &Result); - - /// Checks a result fromt the interpreter. - InterpResult Check(State &Parent, llvm::Expected<bool> &&R); - -private: - /// Current compilation context. - ASTContext &Ctx; - /// Flag to indicate if the use of the interpreter is mandatory. - bool ForceInterp; - /// Interpreter stack, shared across invocations. - InterpStack Stk; - /// Constexpr program. - std::unique_ptr<Program> P; -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/Descriptor.cpp b/clang/lib/AST/Interp/Descriptor.cpp deleted file mode 100644 index 8a602381083..00000000000 --- a/clang/lib/AST/Interp/Descriptor.cpp +++ /dev/null @@ -1,292 +0,0 @@ -//===--- Descriptor.cpp - Types for the constexpr VM ------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Descriptor.h" -#include "Pointer.h" -#include "Record.h" -#include "Type.h" - -using namespace clang; -using namespace clang::interp; - -template <typename T> -static void ctorTy(Block *, char *Ptr, bool, bool, bool, Descriptor *) { - new (Ptr) T(); -} - -template <typename T> static void dtorTy(Block *, char *Ptr, Descriptor *) { - reinterpret_cast<T *>(Ptr)->~T(); -} - -template <typename T> -static void moveTy(Block *, char *Src, char *Dst, Descriptor *) { - auto *SrcPtr = reinterpret_cast<T *>(Src); - auto *DstPtr = reinterpret_cast<T *>(Dst); - new (DstPtr) T(std::move(*SrcPtr)); -} - -template <typename T> -static void ctorArrayTy(Block *, char *Ptr, bool, bool, bool, Descriptor *D) { - for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { - new (&reinterpret_cast<T *>(Ptr)[I]) T(); - } -} - -template <typename T> -static void dtorArrayTy(Block *, char *Ptr, Descriptor *D) { - for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { - reinterpret_cast<T *>(Ptr)[I].~T(); - } -} - -template <typename T> -static void moveArrayTy(Block *, char *Src, char *Dst, Descriptor *D) { - for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { - auto *SrcPtr = &reinterpret_cast<T *>(Src)[I]; - auto *DstPtr = &reinterpret_cast<T *>(Dst)[I]; - new (DstPtr) T(std::move(*SrcPtr)); - } -} - -static void ctorArrayDesc(Block *B, char *Ptr, bool IsConst, bool IsMutable, - bool IsActive, Descriptor *D) { - const unsigned NumElems = D->getNumElems(); - const unsigned ElemSize = - D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); - - unsigned ElemOffset = 0; - for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { - auto *ElemPtr = Ptr + ElemOffset; - auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr); - auto *ElemLoc = reinterpret_cast<char *>(Desc + 1); - auto *SD = D->ElemDesc; - - Desc->Offset = ElemOffset + sizeof(InlineDescriptor); - Desc->Desc = SD; - Desc->IsInitialized = true; - Desc->IsBase = false; - Desc->IsActive = IsActive; - Desc->IsConst = IsConst || D->IsConst; - Desc->IsMutable = IsMutable || D->IsMutable; - if (auto Fn = D->ElemDesc->CtorFn) - Fn(B, ElemLoc, Desc->IsConst, Desc->IsMutable, IsActive, D->ElemDesc); - } -} - -static void dtorArrayDesc(Block *B, char *Ptr, Descriptor *D) { - const unsigned NumElems = D->getNumElems(); - const unsigned ElemSize = - D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); - - unsigned ElemOffset = 0; - for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { - auto *ElemPtr = Ptr + ElemOffset; - auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr); - auto *ElemLoc = reinterpret_cast<char *>(Desc + 1); - if (auto Fn = D->ElemDesc->DtorFn) - Fn(B, ElemLoc, D->ElemDesc); - } -} - -static void moveArrayDesc(Block *B, char *Src, char *Dst, Descriptor *D) { - const unsigned NumElems = D->getNumElems(); - const unsigned ElemSize = - D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); - - unsigned ElemOffset = 0; - for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { - auto *SrcPtr = Src + ElemOffset; - auto *DstPtr = Dst + ElemOffset; - - auto *SrcDesc = reinterpret_cast<InlineDescriptor *>(SrcPtr); - auto *SrcElemLoc = reinterpret_cast<char *>(SrcDesc + 1); - auto *DstDesc = reinterpret_cast<InlineDescriptor *>(DstPtr); - auto *DstElemLoc = reinterpret_cast<char *>(DstDesc + 1); - - *DstDesc = *SrcDesc; - if (auto Fn = D->ElemDesc->MoveFn) - Fn(B, SrcElemLoc, DstElemLoc, D->ElemDesc); - } -} - -static void ctorRecord(Block *B, char *Ptr, bool IsConst, bool IsMutable, - bool IsActive, Descriptor *D) { - const bool IsUnion = D->ElemRecord->isUnion(); - auto CtorSub = [=](unsigned SubOff, Descriptor *F, bool IsBase) { - auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + SubOff) - 1; - Desc->Offset = SubOff; - Desc->Desc = F; - Desc->IsInitialized = (B->isStatic() || F->IsArray) && !IsBase; - Desc->IsBase = IsBase; - Desc->IsActive = IsActive && !IsUnion; - Desc->IsConst = IsConst || F->IsConst; - Desc->IsMutable = IsMutable || F->IsMutable; - if (auto Fn = F->CtorFn) - Fn(B, Ptr + SubOff, Desc->IsConst, Desc->IsMutable, Desc->IsActive, F); - }; - for (const auto &B : D->ElemRecord->bases()) - CtorSub(B.Offset, B.Desc, /*isBase=*/true); - for (const auto &F : D->ElemRecord->fields()) - CtorSub(F.Offset, F.Desc, /*isBase=*/false); - for (const auto &V : D->ElemRecord->virtual_bases()) - CtorSub(V.Offset, V.Desc, /*isBase=*/true); -} - -static void dtorRecord(Block *B, char *Ptr, Descriptor *D) { - auto DtorSub = [=](unsigned SubOff, Descriptor *F) { - if (auto Fn = F->DtorFn) - Fn(B, Ptr + SubOff, F); - }; - for (const auto &F : D->ElemRecord->bases()) - DtorSub(F.Offset, F.Desc); - for (const auto &F : D->ElemRecord->fields()) - DtorSub(F.Offset, F.Desc); - for (const auto &F : D->ElemRecord->virtual_bases()) - DtorSub(F.Offset, F.Desc); -} - -static void moveRecord(Block *B, char *Src, char *Dst, Descriptor *D) { - for (const auto &F : D->ElemRecord->fields()) { - auto FieldOff = F.Offset; - auto FieldDesc = F.Desc; - - *(reinterpret_cast<Descriptor **>(Dst + FieldOff) - 1) = FieldDesc; - if (auto Fn = FieldDesc->MoveFn) - Fn(B, Src + FieldOff, Dst + FieldOff, FieldDesc); - } -} - -static BlockCtorFn getCtorPrim(PrimType Type) { - COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr); -} - -static BlockDtorFn getDtorPrim(PrimType Type) { - COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr); -} - -static BlockMoveFn getMovePrim(PrimType Type) { - COMPOSITE_TYPE_SWITCH(Type, return moveTy<T>, return nullptr); -} - -static BlockCtorFn getCtorArrayPrim(PrimType Type) { - COMPOSITE_TYPE_SWITCH(Type, return ctorArrayTy<T>, return nullptr); -} - -static BlockDtorFn getDtorArrayPrim(PrimType Type) { - COMPOSITE_TYPE_SWITCH(Type, return dtorArrayTy<T>, return nullptr); -} - -static BlockMoveFn getMoveArrayPrim(PrimType Type) { - COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy<T>, return nullptr); -} - -Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsConst, - bool IsTemporary, bool IsMutable) - : Source(D), ElemSize(primSize(Type)), Size(ElemSize), AllocSize(Size), - IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), - CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)), - MoveFn(getMovePrim(Type)) { - assert(Source && "Missing source"); -} - -Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, - bool IsConst, bool IsTemporary, bool IsMutable) - : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), - AllocSize(align(Size) + sizeof(InitMap *)), IsConst(IsConst), - IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true), - CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), - MoveFn(getMoveArrayPrim(Type)) { - assert(Source && "Missing source"); -} - -Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, - UnknownSize) - : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), - AllocSize(alignof(void *)), IsConst(true), IsMutable(false), - IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)), - DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { - assert(Source && "Missing source"); -} - -Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, - bool IsConst, bool IsTemporary, bool IsMutable) - : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), - Size(ElemSize * NumElems), - AllocSize(std::max<size_t>(alignof(void *), Size)), ElemDesc(Elem), - IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), - IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), - MoveFn(moveArrayDesc) { - assert(Source && "Missing source"); -} - -Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, - UnknownSize) - : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), - Size(UnknownSizeMark), AllocSize(alignof(void *)), ElemDesc(Elem), - IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), - CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { - assert(Source && "Missing source"); -} - -Descriptor::Descriptor(const DeclTy &D, Record *R, bool IsConst, - bool IsTemporary, bool IsMutable) - : Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())), - Size(ElemSize), AllocSize(Size), ElemRecord(R), IsConst(IsConst), - IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(ctorRecord), - DtorFn(dtorRecord), MoveFn(moveRecord) { - assert(Source && "Missing source"); -} - -QualType Descriptor::getType() const { - if (auto *E = asExpr()) - return E->getType(); - if (auto *D = asValueDecl()) - return D->getType(); - llvm_unreachable("Invalid descriptor type"); -} - -SourceLocation Descriptor::getLocation() const { - if (auto *D = Source.dyn_cast<const Decl *>()) - return D->getLocation(); - if (auto *E = Source.dyn_cast<const Expr *>()) - return E->getExprLoc(); - llvm_unreachable("Invalid descriptor type"); -} - -InitMap::InitMap(unsigned N) : UninitFields(N) { - for (unsigned I = 0; I < N / PER_FIELD; ++I) { - data()[I] = 0; - } -} - -InitMap::T *InitMap::data() { - auto *Start = reinterpret_cast<char *>(this) + align(sizeof(InitMap)); - return reinterpret_cast<T *>(Start); -} - -bool InitMap::initialize(unsigned I) { - unsigned Bucket = I / PER_FIELD; - unsigned Mask = 1ull << static_cast<uint64_t>(I % PER_FIELD); - if (!(data()[Bucket] & Mask)) { - data()[Bucket] |= Mask; - UninitFields -= 1; - } - return UninitFields == 0; -} - -bool InitMap::isInitialized(unsigned I) { - unsigned Bucket = I / PER_FIELD; - unsigned Mask = 1ull << static_cast<uint64_t>(I % PER_FIELD); - return data()[Bucket] & Mask; -} - -InitMap *InitMap::allocate(unsigned N) { - const size_t NumFields = ((N + PER_FIELD - 1) / PER_FIELD); - const size_t Size = align(sizeof(InitMap)) + NumFields * PER_FIELD; - return new (malloc(Size)) InitMap(N); -} diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h deleted file mode 100644 index b260b760097..00000000000 --- a/clang/lib/AST/Interp/Descriptor.h +++ /dev/null @@ -1,220 +0,0 @@ -//===--- Descriptor.h - Types for the constexpr VM --------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines descriptors which characterise allocations. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_DESCRIPTOR_H -#define LLVM_CLANG_AST_INTERP_DESCRIPTOR_H - -#include "clang/AST/Decl.h" -#include "clang/AST/Expr.h" - -namespace clang { -namespace interp { -class Block; -class Record; -struct Descriptor; -enum PrimType : unsigned; - -using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>; - -/// Invoked whenever a block is created. The constructor method fills in the -/// inline descriptors of all fields and array elements. It also initializes -/// all the fields which contain non-trivial types. -using BlockCtorFn = void (*)(Block *Storage, char *FieldPtr, bool IsConst, - bool IsMutable, bool IsActive, - Descriptor *FieldDesc); - -/// Invoked when a block is destroyed. Invokes the destructors of all -/// non-trivial nested fields of arrays and records. -using BlockDtorFn = void (*)(Block *Storage, char *FieldPtr, - Descriptor *FieldDesc); - -/// Invoked when a block with pointers referencing it goes out of scope. Such -/// blocks are persisted: the move function copies all inline descriptors and -/// non-trivial fields, as existing pointers might need to reference those -/// descriptors. Data is not copied since it cannot be legally read. -using BlockMoveFn = void (*)(Block *Storage, char *SrcFieldPtr, - char *DstFieldPtr, Descriptor *FieldDesc); - -/// Object size as used by the interpreter. -using InterpSize = unsigned; - -/// Describes a memory block created by an allocation site. -struct Descriptor { -private: - /// Original declaration, used to emit the error message. - const DeclTy Source; - /// Size of an element, in host bytes. - const InterpSize ElemSize; - /// Size of the storage, in host bytes. - const InterpSize Size; - /// Size of the allocation (storage + metadata), in host bytes. - const InterpSize AllocSize; - - /// Value to denote arrays of unknown size. - static constexpr unsigned UnknownSizeMark = (unsigned)-1; - -public: - /// Token to denote structures of unknown size. - struct UnknownSize {}; - - /// Pointer to the record, if block contains records. - Record *const ElemRecord = nullptr; - /// Descriptor of the array element. - Descriptor *const ElemDesc = nullptr; - /// Flag indicating if the block is mutable. - const bool IsConst = false; - /// Flag indicating if a field is mutable. - const bool IsMutable = false; - /// Flag indicating if the block is a temporary. - const bool IsTemporary = false; - /// Flag indicating if the block is an array. - const bool IsArray = false; - - /// Storage management methods. - const BlockCtorFn CtorFn = nullptr; - const BlockDtorFn DtorFn = nullptr; - const BlockMoveFn MoveFn = nullptr; - - /// Allocates a descriptor for a primitive. - Descriptor(const DeclTy &D, PrimType Type, bool IsConst, bool IsTemporary, - bool IsMutable); - - /// Allocates a descriptor for an array of primitives. - Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, bool IsConst, - bool IsTemporary, bool IsMutable); - - /// Allocates a descriptor for an array of primitives of unknown size. - Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize); - - /// Allocates a descriptor for an array of composites. - Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, bool IsConst, - bool IsTemporary, bool IsMutable); - - /// Allocates a descriptor for an array of composites of unknown size. - Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize); - - /// Allocates a descriptor for a record. - Descriptor(const DeclTy &D, Record *R, bool IsConst, bool IsTemporary, - bool IsMutable); - - QualType getType() const; - SourceLocation getLocation() const; - - const Decl *asDecl() const { return Source.dyn_cast<const Decl *>(); } - const Expr *asExpr() const { return Source.dyn_cast<const Expr *>(); } - - const ValueDecl *asValueDecl() const { - return dyn_cast_or_null<ValueDecl>(asDecl()); - } - - const FieldDecl *asFieldDecl() const { - return dyn_cast_or_null<FieldDecl>(asDecl()); - } - - const RecordDecl *asRecordDecl() const { - return dyn_cast_or_null<RecordDecl>(asDecl()); - } - - /// Returns the size of the object without metadata. - unsigned getSize() const { - assert(!isUnknownSizeArray() && "Array of unknown size"); - return Size; - } - - /// Returns the allocated size, including metadata. - unsigned getAllocSize() const { return AllocSize; } - /// returns the size of an element when the structure is viewed as an array. - unsigned getElemSize() const { return ElemSize; } - - /// Returns the number of elements stored in the block. - unsigned getNumElems() const { - return Size == UnknownSizeMark ? 0 : (getSize() / getElemSize()); - } - - /// Checks if the descriptor is of an array of primitives. - bool isPrimitiveArray() const { return IsArray && !ElemDesc; } - /// Checks if the descriptor is of an array of zero size. - bool isZeroSizeArray() const { return Size == 0; } - /// Checks if the descriptor is of an array of unknown size. - bool isUnknownSizeArray() const { return Size == UnknownSizeMark; } - - /// Checks if the descriptor is of a primitive. - bool isPrimitive() const { return !IsArray && !ElemRecord; } - - /// Checks if the descriptor is of an array. - bool isArray() const { return IsArray; } -}; - -/// Inline descriptor embedded in structures and arrays. -/// -/// Such descriptors precede all composite array elements and structure fields. -/// If the base of a pointer is not zero, the base points to the end of this -/// structure. The offset field is used to traverse the pointer chain up -/// to the root structure which allocated the object. -struct InlineDescriptor { - /// Offset inside the structure/array. - unsigned Offset; - - /// Flag indicating if the storage is constant or not. - /// Relevant for primitive fields. - unsigned IsConst : 1; - /// For primitive fields, it indicates if the field was initialized. - /// Primitive fields in static storage are always initialized. - /// Arrays are always initialized, even though their elements might not be. - /// Base classes are initialized after the constructor is invoked. - unsigned IsInitialized : 1; - /// Flag indicating if the field is an embedded base class. - unsigned IsBase : 1; - /// Flag indicating if the field is the active member of a union. - unsigned IsActive : 1; - /// Flag indicating if the field is mutable (if in a record). - unsigned IsMutable : 1; - - Descriptor *Desc; -}; - -/// Bitfield tracking the initialisation status of elements of primitive arrays. -/// A pointer to this is embedded at the end of all primitive arrays. -/// If the map was not yet created and nothing was initialied, the pointer to -/// this structure is 0. If the object was fully initialized, the pointer is -1. -struct InitMap { -private: - /// Type packing bits. - using T = uint64_t; - /// Bits stored in a single field. - static constexpr uint64_t PER_FIELD = sizeof(T) * CHAR_BIT; - - /// Initializes the map with no fields set. - InitMap(unsigned N); - - /// Returns a pointer to storage. - T *data(); - -public: - /// Initializes an element. Returns true when object if fully initialized. - bool initialize(unsigned I); - - /// Checks if an element was initialized. - bool isInitialized(unsigned I); - - /// Allocates a map holding N elements. - static InitMap *allocate(unsigned N); - -private: - /// Number of fields initialized. - unsigned UninitFields; -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/Disasm.cpp b/clang/lib/AST/Interp/Disasm.cpp deleted file mode 100644 index 632c8c418fd..00000000000 --- a/clang/lib/AST/Interp/Disasm.cpp +++ /dev/null @@ -1,69 +0,0 @@ -//===--- Disasm.cpp - Disassembler for bytecode functions -------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Dump method for Function which disassembles the bytecode. -// -//===----------------------------------------------------------------------===// - -#include "Function.h" -#include "Opcode.h" -#include "Program.h" -#include "Type.h" -#include "clang/AST/DeclCXX.h" -#include "llvm/Support/Compiler.h" - -using namespace clang; -using namespace clang::interp; - -LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); } - -LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { - if (F) { - if (auto *Cons = dyn_cast<CXXConstructorDecl>(F)) { - const std::string &Name = Cons->getParent()->getNameAsString(); - OS << Name << "::" << Name << ":\n"; - } else { - OS << F->getNameAsString() << ":\n"; - } - } else { - OS << "<<expr>>\n"; - } - - OS << "frame size: " << getFrameSize() << "\n"; - OS << "arg size: " << getArgSize() << "\n"; - OS << "rvo: " << hasRVO() << "\n"; - - auto PrintName = [&OS](const char *Name) { - OS << Name; - for (long I = 0, N = strlen(Name); I < 30 - N; ++I) { - OS << ' '; - } - }; - - for (CodePtr Start = getCodeBegin(), PC = Start; PC != getCodeEnd();) { - size_t Addr = PC - Start; - auto Op = PC.read<Opcode>(); - OS << llvm::format("%8d", Addr) << " "; - switch (Op) { -#define GET_DISASM -#include "Opcodes.inc" -#undef GET_DISASM - } - } -} - -LLVM_DUMP_METHOD void Program::dump() const { dump(llvm::errs()); } - -LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const { - for (auto &Func : Funcs) { - Func.second->dump(); - } - for (auto &Anon : AnonFuncs) { - Anon->dump(); - } -} diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp deleted file mode 100644 index 22e8695b921..00000000000 --- a/clang/lib/AST/Interp/EvalEmitter.cpp +++ /dev/null @@ -1,253 +0,0 @@ -//===--- EvalEmitter.cpp - Instruction emitter for the VM -------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "EvalEmitter.h" -#include "Context.h" -#include "Interp.h" -#include "Opcode.h" -#include "Program.h" -#include "clang/AST/DeclCXX.h" - -using namespace clang; -using namespace clang::interp; - -using APSInt = llvm::APSInt; -template <typename T> using Expected = llvm::Expected<T>; - -EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent, - InterpStack &Stk, APValue &Result) - : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), Result(Result) { - // Create a dummy frame for the interpreter which does not have locals. - S.Current = new InterpFrame(S, nullptr, nullptr, CodePtr(), Pointer()); -} - -llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) { - if (this->visitExpr(E)) - return true; - if (BailLocation) - return llvm::make_error<ByteCodeGenError>(*BailLocation); - return false; -} - -llvm::Expected<bool> EvalEmitter::interpretDecl(const VarDecl *VD) { - if (this->visitDecl(VD)) - return true; - if (BailLocation) - return llvm::make_error<ByteCodeGenError>(*BailLocation); - return false; -} - -void EvalEmitter::emitLabel(LabelTy Label) { - CurrentLabel = Label; -} - -EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; } - -Scope::Local EvalEmitter::createLocal(Descriptor *D) { - // Allocate memory for a local. - auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize()); - auto *B = new (Memory.get()) Block(D, /*isStatic=*/false); - B->invokeCtor(); - - // Register the local. - unsigned Off = Locals.size(); - Locals.insert({Off, std::move(Memory)}); - return {Off, D}; -} - -bool EvalEmitter::bail(const SourceLocation &Loc) { - if (!BailLocation) - BailLocation = Loc; - return false; -} - -bool EvalEmitter::jumpTrue(const LabelTy &Label) { - if (isActive()) { - if (S.Stk.pop<bool>()) - ActiveLabel = Label; - } - return true; -} - -bool EvalEmitter::jumpFalse(const LabelTy &Label) { - if (isActive()) { - if (!S.Stk.pop<bool>()) - ActiveLabel = Label; - } - return true; -} - -bool EvalEmitter::jump(const LabelTy &Label) { - if (isActive()) - CurrentLabel = ActiveLabel = Label; - return true; -} - -bool EvalEmitter::fallthrough(const LabelTy &Label) { - if (isActive()) - ActiveLabel = Label; - CurrentLabel = Label; - return true; -} - -template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) { - if (!isActive()) - return true; - using T = typename PrimConv<OpType>::T; - return ReturnValue<T>(S.Stk.pop<T>(), Result); -} - -bool EvalEmitter::emitRetVoid(const SourceInfo &Info) { return true; } - -bool EvalEmitter::emitRetValue(const SourceInfo &Info) { - // Method to recursively traverse composites. - std::function<bool(QualType, const Pointer &, APValue &)> Composite; - Composite = [this, &Composite](QualType Ty, const Pointer &Ptr, APValue &R) { - if (auto *AT = Ty->getAs<AtomicType>()) - Ty = AT->getValueType(); - - if (auto *RT = Ty->getAs<RecordType>()) { - auto *Record = Ptr.getRecord(); - assert(Record && "Missing record descriptor"); - - bool Ok = true; - if (RT->getDecl()->isUnion()) { - const FieldDecl *ActiveField = nullptr; - APValue Value; - for (auto &F : Record->fields()) { - const Pointer &FP = Ptr.atField(F.Offset); - QualType FieldTy = F.Decl->getType(); - if (FP.isActive()) { - if (llvm::Optional<PrimType> T = Ctx.classify(FieldTy)) { - TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value)); - } else { - Ok &= Composite(FieldTy, FP, Value); - } - break; - } - } - R = APValue(ActiveField, Value); - } else { - unsigned NF = Record->getNumFields(); - unsigned NB = Record->getNumBases(); - unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases(); - - R = APValue(APValue::UninitStruct(), NB, NF); - - for (unsigned I = 0; I < NF; ++I) { - const Record::Field *FD = Record->getField(I); - QualType FieldTy = FD->Decl->getType(); - const Pointer &FP = Ptr.atField(FD->Offset); - APValue &Value = R.getStructField(I); - - if (llvm::Optional<PrimType> T = Ctx.classify(FieldTy)) { - TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value)); - } else { - Ok &= Composite(FieldTy, FP, Value); - } - } - - for (unsigned I = 0; I < NB; ++I) { - const Record::Base *BD = Record->getBase(I); - QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl); - const Pointer &BP = Ptr.atField(BD->Offset); - Ok &= Composite(BaseTy, BP, R.getStructBase(I)); - } - - for (unsigned I = 0; I < NV; ++I) { - const Record::Base *VD = Record->getVirtualBase(I); - QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl); - const Pointer &VP = Ptr.atField(VD->Offset); - Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I)); - } - } - return Ok; - } - if (auto *AT = Ty->getAsArrayTypeUnsafe()) { - const size_t NumElems = Ptr.getNumElems(); - QualType ElemTy = AT->getElementType(); - R = APValue(APValue::UninitArray{}, NumElems, NumElems); - - bool Ok = true; - for (unsigned I = 0; I < NumElems; ++I) { - APValue &Slot = R.getArrayInitializedElt(I); - const Pointer &EP = Ptr.atIndex(I); - if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) { - TYPE_SWITCH(*T, Ok &= ReturnValue<T>(EP.deref<T>(), Slot)); - } else { - Ok &= Composite(ElemTy, EP.narrow(), Slot); - } - } - return Ok; - } - llvm_unreachable("invalid value to return"); - }; - - // Return the composite type. - const auto &Ptr = S.Stk.pop<Pointer>(); - return Composite(Ptr.getType(), Ptr, Result); -} - -bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) { - if (!isActive()) - return true; - - auto It = Locals.find(I); - assert(It != Locals.end() && "Missing local variable"); - S.Stk.push<Pointer>(reinterpret_cast<Block *>(It->second.get())); - return true; -} - -template <PrimType OpType> -bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) { - if (!isActive()) - return true; - - using T = typename PrimConv<OpType>::T; - - auto It = Locals.find(I); - assert(It != Locals.end() && "Missing local variable"); - auto *B = reinterpret_cast<Block *>(It->second.get()); - S.Stk.push<T>(*reinterpret_cast<T *>(B + 1)); - return true; -} - -template <PrimType OpType> -bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) { - if (!isActive()) - return true; - - using T = typename PrimConv<OpType>::T; - - auto It = Locals.find(I); - assert(It != Locals.end() && "Missing local variable"); - auto *B = reinterpret_cast<Block *>(It->second.get()); - *reinterpret_cast<T *>(B + 1) = S.Stk.pop<T>(); - return true; -} - -bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) { - if (!isActive()) - return true; - - for (auto &Local : Descriptors[I]) { - auto It = Locals.find(Local.Offset); - assert(It != Locals.end() && "Missing local variable"); - S.deallocate(reinterpret_cast<Block *>(It->second.get())); - } - - return true; -} - -//===----------------------------------------------------------------------===// -// Opcode evaluators -//===----------------------------------------------------------------------===// - -#define GET_EVAL_IMPL -#include "Opcodes.inc" -#undef GET_EVAL_IMPL diff --git a/clang/lib/AST/Interp/EvalEmitter.h b/clang/lib/AST/Interp/EvalEmitter.h deleted file mode 100644 index 82a638b4dbd..00000000000 --- a/clang/lib/AST/Interp/EvalEmitter.h +++ /dev/null @@ -1,129 +0,0 @@ -//===--- EvalEmitter.h - Instruction emitter for the VM ---------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the instruction emitters. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_EVALEMITTER_H -#define LLVM_CLANG_AST_INTERP_EVALEMITTER_H - -#include "ByteCodeGenError.h" -#include "Context.h" -#include "InterpStack.h" -#include "InterpState.h" -#include "Program.h" -#include "Source.h" -#include "Type.h" -#include "llvm/Support/Error.h" - -namespace clang { -class FunctionDecl; -namespace interp { -class Context; -class Function; -class InterpState; -class Program; -class SourceInfo; -enum Opcode : uint32_t; - -/// An emitter which evaluates opcodes as they are emitted. -class EvalEmitter : public SourceMapper { -public: - using LabelTy = uint32_t; - using AddrTy = uintptr_t; - using Local = Scope::Local; - - llvm::Expected<bool> interpretExpr(const Expr *E); - llvm::Expected<bool> interpretDecl(const VarDecl *VD); - -protected: - EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk, - APValue &Result); - - virtual ~EvalEmitter() {} - - /// Define a label. - void emitLabel(LabelTy Label); - /// Create a label. - LabelTy getLabel(); - - /// Methods implemented by the compiler. - virtual bool visitExpr(const Expr *E) = 0; - virtual bool visitDecl(const VarDecl *VD) = 0; - - bool bail(const Stmt *S) { return bail(S->getBeginLoc()); } - bool bail(const Decl *D) { return bail(D->getBeginLoc()); } - bool bail(const SourceLocation &Loc); - - /// Emits jumps. - bool jumpTrue(const LabelTy &Label); - bool jumpFalse(const LabelTy &Label); - bool jump(const LabelTy &Label); - bool fallthrough(const LabelTy &Label); - - /// Callback for registering a local. - Local createLocal(Descriptor *D); - - /// Returns the source location of the current opcode. - SourceInfo getSource(Function *F, CodePtr PC) const override { - return F ? F->getSource(PC) : CurrentSource; - } - - /// Parameter indices. - llvm::DenseMap<const ParmVarDecl *, unsigned> Params; - /// Local descriptors. - llvm::SmallVector<SmallVector<Local, 8>, 2> Descriptors; - -private: - /// Current compilation context. - Context &Ctx; - /// Current program. - Program &P; - /// Callee evaluation state. - InterpState S; - /// Location to write the result to. - APValue &Result; - - /// Temporaries which require storage. - llvm::DenseMap<unsigned, std::unique_ptr<char[]>> Locals; - - // The emitter always tracks the current instruction and sets OpPC to a token - // value which is mapped to the location of the opcode being evaluated. - CodePtr OpPC; - /// Location of a failure. - llvm::Optional<SourceLocation> BailLocation; - /// Location of the current instruction. - SourceInfo CurrentSource; - - /// Next label ID to generate - first label is 1. - LabelTy NextLabel = 1; - /// Label being executed - 0 is the entry label. - LabelTy CurrentLabel = 0; - /// Active block which should be executed. - LabelTy ActiveLabel = 0; - - /// Since expressions can only jump forward, predicated execution is - /// used to deal with if-else statements. - bool isActive() { return CurrentLabel == ActiveLabel; } - - /// Helper to invoke a method. - bool ExecuteCall(Function *F, Pointer &&This, const SourceInfo &Info); - /// Helper to emit a diagnostic on a missing method. - bool ExecuteNoCall(const FunctionDecl *F, const SourceInfo &Info); - -protected: -#define GET_EVAL_PROTO -#include "Opcodes.inc" -#undef GET_EVAL_PROTO -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/Frame.cpp b/clang/lib/AST/Interp/Frame.cpp deleted file mode 100644 index 16134aa1db3..00000000000 --- a/clang/lib/AST/Interp/Frame.cpp +++ /dev/null @@ -1,14 +0,0 @@ -//===--- Frame.cpp - Call frame for the VM and AST Walker -------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Frame.h" - -using namespace clang; -using namespace clang::interp; - -Frame::~Frame() {} diff --git a/clang/lib/AST/Interp/Frame.h b/clang/lib/AST/Interp/Frame.h deleted file mode 100644 index b9a0ea9412f..00000000000 --- a/clang/lib/AST/Interp/Frame.h +++ /dev/null @@ -1,45 +0,0 @@ -//===--- Frame.h - Call frame for the VM and AST Walker ---------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the base class of interpreter and evaluator stack frames. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_FRAME_H -#define LLVM_CLANG_AST_INTERP_FRAME_H - -#include "clang/Basic/SourceLocation.h" -#include "llvm/Support/raw_ostream.h" - -namespace clang { -class FunctionDecl; - -namespace interp { - -/// Base class for stack frames, shared between VM and walker. -class Frame { -public: - virtual ~Frame(); - - /// Generates a human-readable description of the call site. - virtual void describe(llvm::raw_ostream &OS) = 0; - - /// Returns a pointer to the caller frame. - virtual Frame *getCaller() const = 0; - - /// Returns the location of the call site. - virtual SourceLocation getCallLocation() const = 0; - - /// Returns the called function's declaration. - virtual const FunctionDecl *getCallee() const = 0; -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/Function.cpp b/clang/lib/AST/Interp/Function.cpp deleted file mode 100644 index 0ed13a92aa3..00000000000 --- a/clang/lib/AST/Interp/Function.cpp +++ /dev/null @@ -1,48 +0,0 @@ -//===--- Function.h - Bytecode function for the VM --------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Function.h" -#include "Program.h" -#include "Opcode.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclCXX.h" - -using namespace clang; -using namespace clang::interp; - -Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize, - llvm::SmallVector<PrimType, 8> &&ParamTypes, - llvm::DenseMap<unsigned, ParamDescriptor> &&Params) - : P(P), Loc(F->getBeginLoc()), F(F), ArgSize(ArgSize), - ParamTypes(std::move(ParamTypes)), Params(std::move(Params)) {} - -CodePtr Function::getCodeBegin() const { return Code.data(); } - -CodePtr Function::getCodeEnd() const { return Code.data() + Code.size(); } - -Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const { - auto It = Params.find(Offset); - assert(It != Params.end() && "Invalid parameter offset"); - return It->second; -} - -SourceInfo Function::getSource(CodePtr PC) const { - unsigned Offset = PC - getCodeBegin(); - using Elem = std::pair<unsigned, SourceInfo>; - auto It = std::lower_bound(SrcMap.begin(), SrcMap.end(), Elem{Offset, {}}, - [](Elem A, Elem B) { return A.first < B.first; }); - if (It == SrcMap.end() || It->first != Offset) - llvm::report_fatal_error("missing source location"); - return It->second; -} - -bool Function::isVirtual() const { - if (auto *M = dyn_cast<CXXMethodDecl>(F)) - return M->isVirtual(); - return false; -} diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h deleted file mode 100644 index 28531f04b6e..00000000000 --- a/clang/lib/AST/Interp/Function.h +++ /dev/null @@ -1,163 +0,0 @@ -//===--- Function.h - Bytecode function for the VM --------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the Function class which holds all bytecode function-specific data. -// -// The scope class which describes local variables is also defined here. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_FUNCTION_H -#define LLVM_CLANG_AST_INTERP_FUNCTION_H - -#include "Pointer.h" -#include "Source.h" -#include "clang/AST/Decl.h" -#include "llvm/Support/raw_ostream.h" - -namespace clang { -namespace interp { -class Program; -class ByteCodeEmitter; -enum PrimType : uint32_t; - -/// Describes a scope block. -/// -/// The block gathers all the descriptors of the locals defined in this block. -class Scope { -public: - /// Information about a local's storage. - struct Local { - /// Offset of the local in frame. - unsigned Offset; - /// Descriptor of the local. - Descriptor *Desc; - }; - - using LocalVectorTy = llvm::SmallVector<Local, 8>; - - Scope(LocalVectorTy &&Descriptors) : Descriptors(std::move(Descriptors)) {} - - llvm::iterator_range<LocalVectorTy::iterator> locals() { - return llvm::make_range(Descriptors.begin(), Descriptors.end()); - } - -private: - /// Object descriptors in this block. - LocalVectorTy Descriptors; -}; - -/// Bytecode function. -/// -/// Contains links to the bytecode of the function, as well as metadata -/// describing all arguments and stack-local variables. -class Function { -public: - using ParamDescriptor = std::pair<PrimType, Descriptor *>; - - /// Returns the size of the function's local stack. - unsigned getFrameSize() const { return FrameSize; } - /// Returns the size of the argument stackx - unsigned getArgSize() const { return ArgSize; } - - /// Returns a pointer to the start of the code. - CodePtr getCodeBegin() const; - /// Returns a pointer to the end of the code. - CodePtr getCodeEnd() const; - - /// Returns the original FunctionDecl. - const FunctionDecl *getDecl() const { return F; } - - /// Returns the lcoation. - SourceLocation getLoc() const { return Loc; } - - /// Returns a parameter descriptor. - ParamDescriptor getParamDescriptor(unsigned Offset) const; - - /// Checks if the first argument is a RVO pointer. - bool hasRVO() const { return ParamTypes.size() != Params.size(); } - - /// Range over the scope blocks. - llvm::iterator_range<llvm::SmallVector<Scope, 2>::iterator> scopes() { - return llvm::make_range(Scopes.begin(), Scopes.end()); - } - - /// Range over argument types. - using arg_reverse_iterator = SmallVectorImpl<PrimType>::reverse_iterator; - llvm::iterator_range<arg_reverse_iterator> args_reverse() { - return llvm::make_range(ParamTypes.rbegin(), ParamTypes.rend()); - } - - /// Returns a specific scope. - Scope &getScope(unsigned Idx) { return Scopes[Idx]; } - - /// Returns the source information at a given PC. - SourceInfo getSource(CodePtr PC) const; - - /// Checks if the function is valid to call in constexpr. - bool isConstexpr() const { return IsValid; } - - /// Checks if the function is virtual. - bool isVirtual() const; - - /// Checks if the function is a constructor. - bool isConstructor() const { return isa<CXXConstructorDecl>(F); } - -private: - /// Construct a function representing an actual function. - Function(Program &P, const FunctionDecl *F, unsigned ArgSize, - llvm::SmallVector<PrimType, 8> &&ParamTypes, - llvm::DenseMap<unsigned, ParamDescriptor> &&Params); - - /// Sets the code of a function. - void setCode(unsigned NewFrameSize, std::vector<char> &&NewCode, SourceMap &&NewSrcMap, - llvm::SmallVector<Scope, 2> &&NewScopes) { - FrameSize = NewFrameSize; - Code = std::move(NewCode); - SrcMap = std::move(NewSrcMap); - Scopes = std::move(NewScopes); - IsValid = true; - } - -private: - friend class Program; - friend class ByteCodeEmitter; - - /// Program reference. - Program &P; - /// Location of the executed code. - SourceLocation Loc; - /// Declaration this function was compiled from. - const FunctionDecl *F; - /// Local area size: storage + metadata. - unsigned FrameSize; - /// Size of the argument stack. - unsigned ArgSize; - /// Program code. - std::vector<char> Code; - /// Opcode-to-expression mapping. - SourceMap SrcMap; - /// List of block descriptors. - llvm::SmallVector<Scope, 2> Scopes; - /// List of argument types. - llvm::SmallVector<PrimType, 8> ParamTypes; - /// Map from byte offset to parameter descriptor. - llvm::DenseMap<unsigned, ParamDescriptor> Params; - /// Flag to indicate if the function is valid. - bool IsValid = false; - -public: - /// Dumps the disassembled bytecode to \c llvm::errs(). - void dump() const; - void dump(llvm::raw_ostream &OS) const; -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/Integral.h b/clang/lib/AST/Interp/Integral.h deleted file mode 100644 index 5251920cead..00000000000 --- a/clang/lib/AST/Interp/Integral.h +++ /dev/null @@ -1,308 +0,0 @@ -//===--- Integral.h - Wrapper for numeric types for the VM ------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the VM types and helpers operating on types. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_NUMBER_H -#define LLVM_CLANG_AST_INTERP_NUMBER_H - -#include "clang/AST/ComparisonCategories.h" -#include "clang/AST/APValue.h" -#include "llvm/ADT/APSInt.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/Support/raw_ostream.h" -#include <cstddef> -#include <cstdint> - -namespace clang { -namespace interp { - -using APInt = llvm::APInt; -using APSInt = llvm::APSInt; - -/// Helper to compare two comparable types. -template <typename T> -ComparisonCategoryResult Compare(const T &X, const T &Y) { - if (X < Y) - return ComparisonCategoryResult::Less; - if (X > Y) - return ComparisonCategoryResult::Greater; - return ComparisonCategoryResult::Equal; -} - - -// Helper structure to select the representation. -template <unsigned Bits, bool Signed> struct Repr; - -template <> struct Repr<1, false> { using Type = bool; }; -template <> struct Repr<8, false> { using Type = uint8_t; }; -template <> struct Repr<16, false> { using Type = uint16_t; }; -template <> struct Repr<32, false> { using Type = uint32_t; }; -template <> struct Repr<64, false> { using Type = uint64_t; }; -template <> struct Repr<8, true> { using Type = int8_t; }; -template <> struct Repr<16, true> { using Type = int16_t; }; -template <> struct Repr<32, true> { using Type = int32_t; }; -template <> struct Repr<64, true> { using Type = int64_t; }; - - -/// Helper structure to select the min and max value. -template <unsigned Bits, bool Signed> struct IntegralLimits { - using Type = typename Repr<Bits, Signed>::Type; - static const auto Min = std::numeric_limits<Type>::min(); - static const auto Max = std::numeric_limits<Type>::max(); -}; - -template <> struct IntegralLimits<1, false> { - static const bool Min = false; - static const bool Max = true; -}; - -/// Wrapper around numeric types. -/// -/// These wrappers are required to shared an interface between APSint and -/// builtin primitive numeral types, while optimising for storage and -/// allowing methods operating on primitive type to compile to fast code. -template <unsigned Bits, bool Signed> class Integral { -private: - template <unsigned OtherBits, bool OtherSigned> friend class Integral; - - - // The primitive representing the integral. - using T = typename Repr<Bits, Signed>::Type; - T V; - - /// Primitive representing limits. - using Limit = IntegralLimits<Bits, Signed>; - - /// Construct an integral from anything that is convertible to storage. - template <typename T> explicit Integral(T V) : V(V) {} - -public: - /// Zero-initializes an integral. - Integral() : V(0) {} - - /// Constructs an integral from another integral. - template <unsigned SrcBits, bool SrcSign> - explicit Integral(Integral<SrcBits, SrcSign> V) : V(V.V) {} - - /// Construct an integral from a value based on signedness. - explicit Integral(const APSInt &V) - : V(V.isSigned() ? V.getSExtValue() : V.getZExtValue()) {} - - bool operator<(Integral RHS) const { return V < RHS.V; } - bool operator>(Integral RHS) const { return V > RHS.V; } - bool operator<=(Integral RHS) const { return V <= RHS.V; } - bool operator>=(Integral RHS) const { return V >= RHS.V; } - bool operator==(Integral RHS) const { return V == RHS.V; } - bool operator!=(Integral RHS) const { return V != RHS.V; } - - bool operator>(unsigned RHS) const { - return V >= 0 && static_cast<unsigned>(V) > RHS; - } - - Integral operator+(Integral RHS) const { return Integral(V + RHS.V); } - Integral operator-(Integral RHS) const { return Integral(V - RHS.V); } - Integral operator*(Integral RHS) const { return Integral(V * RHS.V); } - Integral operator/(Integral RHS) const { return Integral(V / RHS.V); } - Integral operator%(Integral RHS) const { return Integral(V % RHS.V); } - Integral operator&(Integral RHS) const { return Integral(V & RHS.V); } - Integral operator|(Integral RHS) const { return Integral(V | RHS.V); } - Integral operator^(Integral RHS) const { return Integral(V ^ RHS.V); } - - Integral operator-() const { return Integral(-V); } - Integral operator~() const { return Integral(~V); } - - Integral operator>>(unsigned RHS) const { return Integral(V >> RHS); } - Integral operator<<(unsigned RHS) const { return Integral(V << RHS); } - - template <unsigned DstBits, bool DstSign> - explicit operator Integral<DstBits, DstSign>() const { - return Integral<DstBits, DstSign>(V); - } - - explicit operator unsigned() const { return V; } - explicit operator int64_t() const { return V; } - explicit operator uint64_t() const { return V; } - - APSInt toAPSInt() const { - return APSInt(APInt(Bits, static_cast<uint64_t>(V), Signed), !Signed); - } - APSInt toAPSInt(unsigned NumBits) const { - if (Signed) - return APSInt(toAPSInt().sextOrTrunc(NumBits), !Signed); - else - return APSInt(toAPSInt().zextOrTrunc(NumBits), !Signed); - } - APValue toAPValue() const { return APValue(toAPSInt()); } - - Integral<Bits, false> toUnsigned() const { - return Integral<Bits, false>(*this); - } - - constexpr static unsigned bitWidth() { return Bits; } - - bool isZero() const { return !V; } - - bool isMin() const { return *this == min(bitWidth()); } - - bool isMinusOne() const { return Signed && V == T(-1); } - - constexpr static bool isSigned() { return Signed; } - - bool isNegative() const { return V < T(0); } - bool isPositive() const { return !isNegative(); } - - ComparisonCategoryResult compare(const Integral &RHS) const { - return Compare(V, RHS.V); - } - - unsigned countLeadingZeros() const { - unsigned LeadingZeros = __builtin_clzll(V); - constexpr auto FullBits = std::numeric_limits<unsigned long long>::digits; - return LeadingZeros - (FullBits - bitWidth()); - } - - Integral truncate(unsigned TruncBits) const { - if (TruncBits >= Bits) - return *this; - const T BitMask = (T(1) << T(TruncBits)) - 1; - const T SignBit = T(1) << (TruncBits - 1); - const T ExtMask = ~BitMask; - return Integral((V & BitMask) | (Signed && (V & SignBit) ? ExtMask : 0)); - } - - void print(llvm::raw_ostream &OS) const { OS << V; } - - static Integral min(unsigned NumBits) { - return Integral(Limit::Min); - } - static Integral max(unsigned NumBits) { - return Integral(Limit::Max); - } - - template <typename T> - static typename std::enable_if<std::is_integral<T>::value, Integral>::type - from(T Value) { - return Integral(Value); - } - - template <unsigned SrcBits, bool SrcSign> - static typename std::enable_if<SrcBits != 0, Integral>::type - from(Integral<SrcBits, SrcSign> Value) { - return Integral(Value.V); - } - - template <bool SrcSign> static Integral from(Integral<0, SrcSign> Value) { - if (SrcSign) - return Integral(Value.V.getSExtValue()); - else - return Integral(Value.V.getZExtValue()); - } - - static Integral zero() { return from(0); } - - template <typename T> static Integral from(T Value, unsigned NumBits) { - return Integral(Value); - } - - static bool inRange(int64_t Value, unsigned NumBits) { - return CheckRange<T, Limit::Min, Limit::Max>(Value); - } - - static bool increment(Integral A, Integral *R) { - return add(A, Integral(T(1)), A.bitWidth(), R); - } - - static bool decrement(Integral A, Integral *R) { - return sub(A, Integral(T(1)), A.bitWidth(), R); - } - - static bool add(Integral A, Integral B, unsigned OpBits, Integral *R) { - return CheckAddUB(A.V, B.V, R->V); - } - - static bool sub(Integral A, Integral B, unsigned OpBits, Integral *R) { - return CheckSubUB(A.V, B.V, R->V); - } - - static bool mul(Integral A, Integral B, unsigned OpBits, Integral *R) { - return CheckMulUB(A.V, B.V, R->V); - } - -private: - template <typename T> - static typename std::enable_if<std::is_signed<T>::value, bool>::type - CheckAddUB(T A, T B, T &R) { - return llvm::AddOverflow<T>(A, B, R); - } - - template <typename T> - static typename std::enable_if<std::is_unsigned<T>::value, bool>::type - CheckAddUB(T A, T B, T &R) { - R = A + B; - return false; - } - - template <typename T> - static typename std::enable_if<std::is_signed<T>::value, bool>::type - CheckSubUB(T A, T B, T &R) { - return llvm::SubOverflow<T>(A, B, R); - } - - template <typename T> - static typename std::enable_if<std::is_unsigned<T>::value, bool>::type - CheckSubUB(T A, T B, T &R) { - R = A - B; - return false; - } - - template <typename T> - static typename std::enable_if<std::is_signed<T>::value, bool>::type - CheckMulUB(T A, T B, T &R) { - return llvm::MulOverflow<T>(A, B, R); - } - - template <typename T> - static typename std::enable_if<std::is_unsigned<T>::value, bool>::type - CheckMulUB(T A, T B, T &R) { - R = A * B; - return false; - } - - template <typename T, T Min, T Max> - static typename std::enable_if<std::is_signed<T>::value, bool>::type - CheckRange(int64_t V) { - return Min <= V && V <= Max; - } - - template <typename T, T Min, T Max> - static typename std::enable_if<std::is_unsigned<T>::value, bool>::type - CheckRange(int64_t V) { - return V >= 0 && static_cast<uint64_t>(V) <= Max; - } -}; - -template <unsigned Bits, bool Signed> -llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Integral<Bits, Signed> I) { - I.print(OS); - return OS; -} - -template <> -inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - Integral<1, false> B) { - OS << (B.isZero() ? "false" : "true"); - return OS; -} - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp deleted file mode 100644 index d60a4274b8d..00000000000 --- a/clang/lib/AST/Interp/Interp.cpp +++ /dev/null @@ -1,417 +0,0 @@ -//===--- InterpState.cpp - Interpreter for the constexpr VM -----*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Interp.h" -#include "Function.h" -#include "InterpFrame.h" -#include "InterpStack.h" -#include "Opcode.h" -#include "Program.h" -#include "State.h" -#include "Type.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/ASTDiagnostic.h" -#include "clang/AST/CXXInheritance.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ExprCXX.h" -#include "llvm/ADT/APSInt.h" -#include <limits> -#include <vector> - -using namespace clang; -using namespace clang::interp; - -//===----------------------------------------------------------------------===// -// Ret -//===----------------------------------------------------------------------===// - -template <PrimType Name, class T = typename PrimConv<Name>::T> -static 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"); - if (!S.checkingPotentialConstantExpression()) - S.Current->popArgs(); - - if (InterpFrame *Caller = S.Current->Caller) { - PC = S.Current->getRetPC(); - delete S.Current; - S.Current = Caller; - S.Stk.push<T>(Ret); - } else { - delete S.Current; - S.Current = nullptr; - if (!ReturnValue<T>(Ret, Result)) - return false; - } - return true; -} - -static bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) { - S.CallStackDepth--; - - assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame"); - if (!S.checkingPotentialConstantExpression()) - S.Current->popArgs(); - - if (InterpFrame *Caller = S.Current->Caller) { - PC = S.Current->getRetPC(); - delete S.Current; - S.Current = Caller; - } else { - delete S.Current; - S.Current = nullptr; - } - return true; -} - -static bool RetValue(InterpState &S, CodePtr &Pt, APValue &Result) { - llvm::report_fatal_error("Interpreter cannot return values"); -} - -//===----------------------------------------------------------------------===// -// Jmp, Jt, Jf -//===----------------------------------------------------------------------===// - -static bool Jmp(InterpState &S, CodePtr &PC, int32_t Offset) { - PC += Offset; - return true; -} - -static bool Jt(InterpState &S, CodePtr &PC, int32_t Offset) { - if (S.Stk.pop<bool>()) { - PC += Offset; - } - return true; -} - -static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) { - if (!S.Stk.pop<bool>()) { - PC += Offset; - } - return true; -} - -static bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - AccessKinds AK) { - if (Ptr.isInitialized()) - return true; - if (!S.checkingPotentialConstantExpression()) { - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_access_uninit) << AK << false; - } - return false; -} - -static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - AccessKinds AK) { - if (Ptr.isActive()) - return true; - - // Get the inactive field descriptor. - const FieldDecl *InactiveField = Ptr.getField(); - - // Walk up the pointer chain to find the union which is not active. - Pointer U = Ptr.getBase(); - while (!U.isActive()) { - U = U.getBase(); - } - - // Find the active field of the union. - Record *R = U.getRecord(); - assert(R && R->isUnion() && "Not a union"); - const FieldDecl *ActiveField = nullptr; - for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) { - const Pointer &Field = U.atField(R->getField(I)->Offset); - if (Field.isActive()) { - ActiveField = Field.getField(); - break; - } - } - - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_access_inactive_union_member) - << AK << InactiveField << !ActiveField << ActiveField; - return false; -} - -static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - AccessKinds AK) { - if (auto ID = Ptr.getDeclID()) { - if (!Ptr.isStaticTemporary()) - return true; - - if (Ptr.getDeclDesc()->getType().isConstQualified()) - return true; - - if (S.P.getCurrentDecl() == ID) - return true; - - const SourceInfo &E = S.Current->getSource(OpPC); - S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK; - S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here); - return false; - } - return true; -} - -static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - if (auto ID = Ptr.getDeclID()) { - if (!Ptr.isStatic()) - return true; - - if (S.P.getCurrentDecl() == ID) - return true; - - S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_modify_global); - return false; - } - return true; -} - -namespace clang { -namespace interp { - -bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - if (!Ptr.isExtern()) - return true; - - if (!S.checkingPotentialConstantExpression()) { - auto *VD = Ptr.getDeclDesc()->asValueDecl(); - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_ltor_non_constexpr, 1) << VD; - S.Note(VD->getLocation(), diag::note_declared_at); - } - return false; -} - -bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - if (!Ptr.isUnknownSizeArray()) - return true; - const SourceInfo &E = S.Current->getSource(OpPC); - S.FFDiag(E, diag::note_constexpr_unsized_array_indexed); - return false; -} - -bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - AccessKinds AK) { - const auto &Src = S.Current->getSource(OpPC); - if (Ptr.isZero()) { - - if (Ptr.isField()) - S.FFDiag(Src, diag::note_constexpr_null_subobject) << CSK_Field; - else - S.FFDiag(Src, diag::note_constexpr_access_null) << AK; - - return false; - } - - if (!Ptr.isLive()) { - bool IsTemp = Ptr.isTemporary(); - - S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp; - - if (IsTemp) - S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here); - else - S.Note(Ptr.getDeclLoc(), diag::note_declared_at); - - return false; - } - - return true; -} - -bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - CheckSubobjectKind CSK) { - if (!Ptr.isZero()) - return true; - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_null_subobject) << CSK; - return false; -} - -bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - AccessKinds AK) { - if (!Ptr.isOnePastEnd()) - return true; - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_access_past_end) << AK; - return false; -} - -bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - CheckSubobjectKind CSK) { - if (!Ptr.isElementPastEnd()) - return true; - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) << CSK; - return false; -} - -bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - assert(Ptr.isLive() && "Pointer is not live"); - if (!Ptr.isConst()) { - return true; - } - - const QualType Ty = Ptr.getType(); - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_modify_const_type) << Ty; - return false; -} - -bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - assert(Ptr.isLive() && "Pointer is not live"); - if (!Ptr.isMutable()) { - return true; - } - - const SourceInfo &Loc = S.Current->getSource(OpPC); - const FieldDecl *Field = Ptr.getField(); - S.FFDiag(Loc, diag::note_constexpr_ltor_mutable, 1) << Field; - S.Note(Field->getLocation(), diag::note_declared_at); - return false; -} - -bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - if (!CheckLive(S, OpPC, Ptr, AK_Read)) - return false; - if (!CheckExtern(S, OpPC, Ptr)) - return false; - if (!CheckRange(S, OpPC, Ptr, AK_Read)) - return false; - if (!CheckInitialized(S, OpPC, Ptr, AK_Read)) - return false; - if (!CheckActive(S, OpPC, Ptr, AK_Read)) - return false; - if (!CheckTemporary(S, OpPC, Ptr, AK_Read)) - return false; - if (!CheckMutable(S, OpPC, Ptr)) - return false; - return true; -} - -bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - if (!CheckLive(S, OpPC, Ptr, AK_Assign)) - return false; - if (!CheckExtern(S, OpPC, Ptr)) - return false; - if (!CheckRange(S, OpPC, Ptr, AK_Assign)) - return false; - if (!CheckGlobal(S, OpPC, Ptr)) - return false; - if (!CheckConst(S, OpPC, Ptr)) - return false; - return true; -} - -bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - if (!CheckLive(S, OpPC, Ptr, AK_MemberCall)) - return false; - if (!CheckExtern(S, OpPC, Ptr)) - return false; - if (!CheckRange(S, OpPC, Ptr, AK_MemberCall)) - return false; - return true; -} - -bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - if (!CheckLive(S, OpPC, Ptr, AK_Assign)) - return false; - if (!CheckRange(S, OpPC, Ptr, AK_Assign)) - return false; - return true; -} - -bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F) { - const SourceLocation &Loc = S.Current->getLocation(OpPC); - - if (F->isVirtual()) { - if (!S.getLangOpts().CPlusPlus2a) { - S.CCEDiag(Loc, diag::note_constexpr_virtual_call); - return false; - } - } - - if (!F->isConstexpr()) { - if (S.getLangOpts().CPlusPlus11) { - const FunctionDecl *DiagDecl = F->getDecl(); - - // If this function is not constexpr because it is an inherited - // non-constexpr constructor, diagnose that directly. - auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl); - if (CD && CD->isInheritingConstructor()) { - auto *Inherited = CD->getInheritedConstructor().getConstructor(); - if (!Inherited->isConstexpr()) - DiagDecl = CD = Inherited; - } - - // FIXME: If DiagDecl is an implicitly-declared special member function - // or an inheriting constructor, we should be much more explicit about why - // it's not constexpr. - if (CD && CD->isInheritingConstructor()) - S.FFDiag(Loc, diag::note_constexpr_invalid_inhctor, 1) - << CD->getInheritedConstructor().getConstructor()->getParent(); - else - S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1) - << DiagDecl->isConstexpr() << (bool)CD << DiagDecl; - S.Note(DiagDecl->getLocation(), diag::note_declared_at); - } else { - S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr); - } - return false; - } - - return true; -} - -bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This) { - if (!This.isZero()) - return true; - - const SourceInfo &Loc = S.Current->getSource(OpPC); - - bool IsImplicit = false; - if (auto *E = dyn_cast_or_null<CXXThisExpr>(Loc.asExpr())) - IsImplicit = E->isImplicit(); - - if (S.getLangOpts().CPlusPlus11) - S.FFDiag(Loc, diag::note_constexpr_this) << IsImplicit; - else - S.FFDiag(Loc); - - return false; -} - -bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) { - if (!MD->isPure()) - return true; - const SourceInfo &E = S.Current->getSource(OpPC); - S.FFDiag(E, diag::note_constexpr_pure_virtual_call, 1) << MD; - S.Note(MD->getLocation(), diag::note_declared_at); - return false; -} -bool Interpret(InterpState &S, APValue &Result) { - CodePtr PC = S.Current->getPC(); - - for (;;) { - auto Op = PC.read<Opcode>(); - CodePtr OpPC = PC; - - switch (Op) { -#define GET_INTERP -#include "Opcodes.inc" -#undef GET_INTERP - } - } -} - -} // namespace interp -} // namespace clang diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h deleted file mode 100644 index 24c7fb99741..00000000000 --- a/clang/lib/AST/Interp/Interp.h +++ /dev/null @@ -1,960 +0,0 @@ -//===--- Interp.h - Interpreter for the constexpr VM ------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Definition of the interpreter state and entry point. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_INTERP_H -#define LLVM_CLANG_AST_INTERP_INTERP_H - -#include "Function.h" -#include "InterpFrame.h" -#include "InterpStack.h" -#include "InterpState.h" -#include "Opcode.h" -#include "Program.h" -#include "State.h" -#include "Type.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/ASTDiagnostic.h" -#include "clang/AST/CXXInheritance.h" -#include "clang/AST/Expr.h" -#include "llvm/ADT/APFloat.h" -#include "llvm/ADT/APSInt.h" -#include "llvm/Support/Endian.h" -#include <limits> -#include <vector> - -namespace clang { -namespace interp { - -using APInt = llvm::APInt; -using APSInt = llvm::APSInt; - -/// Convers a value to an APValue. -template <typename T> bool ReturnValue(const T &V, APValue &R) { - R = V.toAPValue(); - return true; -} - -/// Checks if the variable has externally defined storage. -bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr); - -/// Checks if the array is offsetable. -bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr); - -/// Checks if a pointer is live and accesible. -bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - AccessKinds AK); -/// Checks if a pointer is null. -bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - CheckSubobjectKind CSK); - -/// Checks if a pointer is in range. -bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - AccessKinds AK); - -/// Checks if a field from which a pointer is going to be derived is valid. -bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, - CheckSubobjectKind CSK); - -/// Checks if a pointer points to const storage. -bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr); - -/// Checks if a pointer points to a mutable field. -bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr); - -/// Checks if a value can be loaded from a block. -bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr); - -/// Checks if a value can be stored in a block. -bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr); - -/// Checks if a method can be invoked on an object. -bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr); - -/// Checks if a value can be initialized. -bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr); - -/// Checks if a method can be called. -bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F); - -/// Checks the 'this' pointer. -bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This); - -/// Checks if a method is pure virtual. -bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD); - -template <typename T> inline bool IsTrue(const T &V) { return !V.isZero(); } - -//===----------------------------------------------------------------------===// -// Add, Sub, Mul -//===----------------------------------------------------------------------===// - -template <typename T, bool (*OpFW)(T, T, unsigned, T *), - template <typename U> class OpAP> -bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS, - const T &RHS) { - // Fast path - add the numbers with fixed width. - T Result; - if (!OpFW(LHS, RHS, Bits, &Result)) { - S.Stk.push<T>(Result); - return true; - } - - // If for some reason evaluation continues, use the truncated results. - S.Stk.push<T>(Result); - - // Slow path - compute the result using another bit of precision. - APSInt Value = OpAP<APSInt>()(LHS.toAPSInt(Bits), RHS.toAPSInt(Bits)); - - // Report undefined behaviour, stopping if required. - const Expr *E = S.Current->getExpr(OpPC); - QualType Type = E->getType(); - if (S.checkingForOverflow()) { - auto Trunc = Value.trunc(Result.bitWidth()).toString(10); - auto Loc = E->getExprLoc(); - S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type; - return true; - } else { - S.CCEDiag(E, diag::note_constexpr_overflow) << Value << Type; - return S.noteUndefinedBehavior(); - } -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool Add(InterpState &S, CodePtr OpPC) { - const T &RHS = S.Stk.pop<T>(); - const T &LHS = S.Stk.pop<T>(); - const unsigned Bits = RHS.bitWidth() + 1; - return AddSubMulHelper<T, T::add, std::plus>(S, OpPC, Bits, LHS, RHS); -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool Sub(InterpState &S, CodePtr OpPC) { - const T &RHS = S.Stk.pop<T>(); - const T &LHS = S.Stk.pop<T>(); - const unsigned Bits = RHS.bitWidth() + 1; - return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, Bits, LHS, RHS); -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool Mul(InterpState &S, CodePtr OpPC) { - const T &RHS = S.Stk.pop<T>(); - const T &LHS = S.Stk.pop<T>(); - const unsigned Bits = RHS.bitWidth() * 2; - return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS); -} - -//===----------------------------------------------------------------------===// -// EQ, NE, GT, GE, LT, LE -//===----------------------------------------------------------------------===// - -using CompareFn = llvm::function_ref<bool(ComparisonCategoryResult)>; - -template <typename T> -bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) { - using BoolT = PrimConv<PT_Bool>::T; - const T &RHS = S.Stk.pop<T>(); - const T &LHS = S.Stk.pop<T>(); - S.Stk.push<BoolT>(BoolT::from(Fn(LHS.compare(RHS)))); - return true; -} - -template <typename T> -bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) { - return CmpHelper<T>(S, OpPC, Fn); -} - -template <> -inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) { - using BoolT = PrimConv<PT_Bool>::T; - const Pointer &RHS = S.Stk.pop<Pointer>(); - const Pointer &LHS = S.Stk.pop<Pointer>(); - - if (!Pointer::hasSameBase(LHS, RHS)) { - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr); - return false; - } else { - unsigned VL = LHS.getByteOffset(); - unsigned VR = RHS.getByteOffset(); - S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR)))); - return true; - } -} - -template <> -inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) { - using BoolT = PrimConv<PT_Bool>::T; - const Pointer &RHS = S.Stk.pop<Pointer>(); - const Pointer &LHS = S.Stk.pop<Pointer>(); - - if (LHS.isZero() || RHS.isZero()) { - if (LHS.isZero() && RHS.isZero()) - S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Equal))); - else - S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Nonequal))); - return true; - } - - if (!Pointer::hasSameBase(LHS, RHS)) { - S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Unordered))); - return true; - } else { - unsigned VL = LHS.getByteOffset(); - unsigned VR = RHS.getByteOffset(); - S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR)))); - return true; - } -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool EQ(InterpState &S, CodePtr OpPC) { - return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) { - return R == ComparisonCategoryResult::Equal; - }); -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool NE(InterpState &S, CodePtr OpPC) { - return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) { - return R != ComparisonCategoryResult::Equal; - }); -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool LT(InterpState &S, CodePtr OpPC) { - return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) { - return R == ComparisonCategoryResult::Less; - }); -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool LE(InterpState &S, CodePtr OpPC) { - return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) { - return R == ComparisonCategoryResult::Less || - R == ComparisonCategoryResult::Equal; - }); -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool GT(InterpState &S, CodePtr OpPC) { - return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) { - return R == ComparisonCategoryResult::Greater; - }); -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool GE(InterpState &S, CodePtr OpPC) { - return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) { - return R == ComparisonCategoryResult::Greater || - R == ComparisonCategoryResult::Equal; - }); -} - -//===----------------------------------------------------------------------===// -// InRange -//===----------------------------------------------------------------------===// - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool InRange(InterpState &S, CodePtr OpPC) { - const T RHS = S.Stk.pop<T>(); - const T LHS = S.Stk.pop<T>(); - const T Value = S.Stk.pop<T>(); - - S.Stk.push<bool>(LHS <= Value && Value <= RHS); - return true; -} - -//===----------------------------------------------------------------------===// -// Dup, Pop, Test -//===----------------------------------------------------------------------===// - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool Dup(InterpState &S, CodePtr OpPC) { - S.Stk.push<T>(S.Stk.peek<T>()); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool Pop(InterpState &S, CodePtr OpPC) { - S.Stk.pop<T>(); - return true; -} - -//===----------------------------------------------------------------------===// -// Const -//===----------------------------------------------------------------------===// - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool Const(InterpState &S, CodePtr OpPC, const T &Arg) { - S.Stk.push<T>(Arg); - return true; -} - -//===----------------------------------------------------------------------===// -// Get/Set Local/Param/Global/This -//===----------------------------------------------------------------------===// - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { - S.Stk.push<T>(S.Current->getLocal<T>(I)); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool SetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { - S.Current->setLocal<T>(I, S.Stk.pop<T>()); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool GetParam(InterpState &S, CodePtr OpPC, uint32_t I) { - if (S.checkingPotentialConstantExpression()) { - return false; - } - S.Stk.push<T>(S.Current->getParam<T>(I)); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool SetParam(InterpState &S, CodePtr OpPC, uint32_t I) { - S.Current->setParam<T>(I, S.Stk.pop<T>()); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) { - const Pointer &Obj = S.Stk.peek<Pointer>(); - if (!CheckNull(S, OpPC, Obj, CSK_Field)) - return false; - if (!CheckRange(S, OpPC, Obj, CSK_Field)) - return false; - const Pointer &Field = Obj.atField(I); - if (!CheckLoad(S, OpPC, Field)) - return false; - S.Stk.push<T>(Field.deref<T>()); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) { - const T &Value = S.Stk.pop<T>(); - const Pointer &Obj = S.Stk.peek<Pointer>(); - if (!CheckNull(S, OpPC, Obj, CSK_Field)) - return false; - if (!CheckRange(S, OpPC, Obj, CSK_Field)) - return false; - const Pointer &Field = Obj.atField(I); - if (!CheckStore(S, OpPC, Field)) - return false; - Field.deref<T>() = Value; - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) { - const Pointer &Obj = S.Stk.pop<Pointer>(); - if (!CheckNull(S, OpPC, Obj, CSK_Field)) - return false; - if (!CheckRange(S, OpPC, Obj, CSK_Field)) - return false; - const Pointer &Field = Obj.atField(I); - if (!CheckLoad(S, OpPC, Field)) - return false; - S.Stk.push<T>(Field.deref<T>()); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool GetThisField(InterpState &S, CodePtr OpPC, uint32_t I) { - if (S.checkingPotentialConstantExpression()) - return false; - const Pointer &This = S.Current->getThis(); - if (!CheckThis(S, OpPC, This)) - return false; - const Pointer &Field = This.atField(I); - if (!CheckLoad(S, OpPC, Field)) - return false; - S.Stk.push<T>(Field.deref<T>()); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) { - if (S.checkingPotentialConstantExpression()) - return false; - const T &Value = S.Stk.pop<T>(); - const Pointer &This = S.Current->getThis(); - if (!CheckThis(S, OpPC, This)) - return false; - const Pointer &Field = This.atField(I); - if (!CheckStore(S, OpPC, Field)) - return false; - Field.deref<T>() = Value; - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { - auto *B = S.P.getGlobal(I); - if (B->isExtern()) - return false; - S.Stk.push<T>(B->deref<T>()); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { - // TODO: emit warning. - return false; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { - S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>(); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) { - if (S.checkingPotentialConstantExpression()) - return false; - const Pointer &This = S.Current->getThis(); - if (!CheckThis(S, OpPC, This)) - return false; - const Pointer &Field = This.atField(I); - Field.deref<T>() = S.Stk.pop<T>(); - Field.initialize(); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) { - if (S.checkingPotentialConstantExpression()) - return false; - const Pointer &This = S.Current->getThis(); - if (!CheckThis(S, OpPC, This)) - return false; - const Pointer &Field = This.atField(F->Offset); - const auto &Value = S.Stk.pop<T>(); - Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx())); - Field.initialize(); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) { - if (S.checkingPotentialConstantExpression()) - return false; - const Pointer &This = S.Current->getThis(); - if (!CheckThis(S, OpPC, This)) - return false; - const Pointer &Field = This.atField(I); - Field.deref<T>() = S.Stk.pop<T>(); - Field.activate(); - Field.initialize(); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) { - const T &Value = S.Stk.pop<T>(); - const Pointer &Field = S.Stk.pop<Pointer>().atField(I); - Field.deref<T>() = Value; - Field.activate(); - Field.initialize(); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) { - const T &Value = S.Stk.pop<T>(); - const Pointer &Field = S.Stk.pop<Pointer>().atField(F->Offset); - Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx())); - Field.activate(); - Field.initialize(); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool InitFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) { - const T &Value = S.Stk.pop<T>(); - const Pointer &Ptr = S.Stk.pop<Pointer>(); - const Pointer &Field = Ptr.atField(I); - Field.deref<T>() = Value; - Field.activate(); - Field.initialize(); - return true; -} - -//===----------------------------------------------------------------------===// -// GetPtr Local/Param/Global/Field/This -//===----------------------------------------------------------------------===// - -inline bool GetPtrLocal(InterpState &S, CodePtr OpPC, uint32_t I) { - S.Stk.push<Pointer>(S.Current->getLocalPointer(I)); - return true; -} - -inline bool GetPtrParam(InterpState &S, CodePtr OpPC, uint32_t I) { - if (S.checkingPotentialConstantExpression()) { - return false; - } - S.Stk.push<Pointer>(S.Current->getParamPointer(I)); - return true; -} - -inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { - S.Stk.push<Pointer>(S.P.getPtrGlobal(I)); - return true; -} - -inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) { - const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckNull(S, OpPC, Ptr, CSK_Field)) - return false; - if (!CheckExtern(S, OpPC, Ptr)) - return false; - if (!CheckRange(S, OpPC, Ptr, CSK_Field)) - return false; - S.Stk.push<Pointer>(Ptr.atField(Off)); - return true; -} - -inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) { - if (S.checkingPotentialConstantExpression()) - return false; - const Pointer &This = S.Current->getThis(); - if (!CheckThis(S, OpPC, This)) - return false; - S.Stk.push<Pointer>(This.atField(Off)); - return true; -} - -inline bool GetPtrActiveField(InterpState &S, CodePtr OpPC, uint32_t Off) { - const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckNull(S, OpPC, Ptr, CSK_Field)) - return false; - if (!CheckRange(S, OpPC, Ptr, CSK_Field)) - return false; - Pointer Field = Ptr.atField(Off); - Ptr.deactivate(); - Field.activate(); - S.Stk.push<Pointer>(std::move(Field)); - return true; -} - -inline bool GetPtrActiveThisField(InterpState &S, CodePtr OpPC, uint32_t Off) { - if (S.checkingPotentialConstantExpression()) - return false; - const Pointer &This = S.Current->getThis(); - if (!CheckThis(S, OpPC, This)) - return false; - Pointer Field = This.atField(Off); - This.deactivate(); - Field.activate(); - S.Stk.push<Pointer>(std::move(Field)); - return true; -} - -inline bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) { - const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckNull(S, OpPC, Ptr, CSK_Base)) - return false; - S.Stk.push<Pointer>(Ptr.atField(Off)); - return true; -} - -inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) { - if (S.checkingPotentialConstantExpression()) - return false; - const Pointer &This = S.Current->getThis(); - if (!CheckThis(S, OpPC, This)) - return false; - S.Stk.push<Pointer>(This.atField(Off)); - return true; -} - -inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl, - const Pointer &Ptr) { - Pointer Base = Ptr; - while (Base.isBaseClass()) - Base = Base.getBase(); - - auto *Field = Base.getRecord()->getVirtualBase(Decl); - S.Stk.push<Pointer>(Base.atField(Field->Offset)); - return true; -} - -inline bool GetPtrVirtBase(InterpState &S, CodePtr OpPC, const RecordDecl *D) { - const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckNull(S, OpPC, Ptr, CSK_Base)) - return false; - return VirtBaseHelper(S, OpPC, D, Ptr); -} - -inline bool GetPtrThisVirtBase(InterpState &S, CodePtr OpPC, - const RecordDecl *D) { - if (S.checkingPotentialConstantExpression()) - return false; - const Pointer &This = S.Current->getThis(); - if (!CheckThis(S, OpPC, This)) - return false; - return VirtBaseHelper(S, OpPC, D, S.Current->getThis()); -} - -//===----------------------------------------------------------------------===// -// Load, Store, Init -//===----------------------------------------------------------------------===// - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool Load(InterpState &S, CodePtr OpPC) { - const Pointer &Ptr = S.Stk.peek<Pointer>(); - if (!CheckLoad(S, OpPC, Ptr)) - return false; - S.Stk.push<T>(Ptr.deref<T>()); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool LoadPop(InterpState &S, CodePtr OpPC) { - const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckLoad(S, OpPC, Ptr)) - return false; - S.Stk.push<T>(Ptr.deref<T>()); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool Store(InterpState &S, CodePtr OpPC) { - const T &Value = S.Stk.pop<T>(); - const Pointer &Ptr = S.Stk.peek<Pointer>(); - if (!CheckStore(S, OpPC, Ptr)) - return false; - Ptr.deref<T>() = Value; - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool StorePop(InterpState &S, CodePtr OpPC) { - const T &Value = S.Stk.pop<T>(); - const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckStore(S, OpPC, Ptr)) - return false; - Ptr.deref<T>() = Value; - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool StoreBitField(InterpState &S, CodePtr OpPC) { - const T &Value = S.Stk.pop<T>(); - const Pointer &Ptr = S.Stk.peek<Pointer>(); - if (!CheckStore(S, OpPC, Ptr)) - return false; - if (auto *FD = Ptr.getField()) { - Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx())); - } else { - Ptr.deref<T>() = Value; - } - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) { - const T &Value = S.Stk.pop<T>(); - const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckStore(S, OpPC, Ptr)) - return false; - if (auto *FD = Ptr.getField()) { - Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx())); - } else { - Ptr.deref<T>() = Value; - } - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool InitPop(InterpState &S, CodePtr OpPC) { - const T &Value = S.Stk.pop<T>(); - const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckInit(S, OpPC, Ptr)) - return false; - Ptr.initialize(); - new (&Ptr.deref<T>()) T(Value); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) { - const T &Value = S.Stk.pop<T>(); - const Pointer &Ptr = S.Stk.peek<Pointer>().atIndex(Idx); - if (!CheckInit(S, OpPC, Ptr)) - return false; - Ptr.initialize(); - new (&Ptr.deref<T>()) T(Value); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) { - const T &Value = S.Stk.pop<T>(); - const Pointer &Ptr = S.Stk.pop<Pointer>().atIndex(Idx); - if (!CheckInit(S, OpPC, Ptr)) - return false; - Ptr.initialize(); - new (&Ptr.deref<T>()) T(Value); - return true; -} - -//===----------------------------------------------------------------------===// -// AddOffset, SubOffset -//===----------------------------------------------------------------------===// - -template <class T, bool Add> bool OffsetHelper(InterpState &S, CodePtr OpPC) { - // Fetch the pointer and the offset. - const T &Offset = S.Stk.pop<T>(); - const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex)) - return false; - if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer)) - return false; - - // Get a version of the index comparable to the type. - T Index = T::from(Ptr.getIndex(), Offset.bitWidth()); - // A zero offset does not change the pointer, but in the case of an array - // it has to be adjusted to point to the first element instead of the array. - if (Offset.isZero()) { - S.Stk.push<Pointer>(Index.isZero() ? Ptr.atIndex(0) : Ptr); - return true; - } - // Arrays of unknown bounds cannot have pointers into them. - if (!CheckArray(S, OpPC, Ptr)) - return false; - - // Compute the largest index into the array. - unsigned MaxIndex = Ptr.getNumElems(); - - // Helper to report an invalid offset, computed as APSInt. - auto InvalidOffset = [&]() { - const unsigned Bits = Offset.bitWidth(); - APSInt APOffset(Offset.toAPSInt().extend(Bits + 2), false); - APSInt APIndex(Index.toAPSInt().extend(Bits + 2), false); - APSInt NewIndex = Add ? (APIndex + APOffset) : (APIndex - APOffset); - S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index) - << NewIndex - << /*array*/ static_cast<int>(!Ptr.inArray()) - << static_cast<unsigned>(MaxIndex); - return false; - }; - - // If the new offset would be negative, bail out. - if (Add && Offset.isNegative() && (Offset.isMin() || -Offset > Index)) - return InvalidOffset(); - if (!Add && Offset.isPositive() && Index < Offset) - return InvalidOffset(); - - // If the new offset would be out of bounds, bail out. - unsigned MaxOffset = MaxIndex - Ptr.getIndex(); - if (Add && Offset.isPositive() && Offset > MaxOffset) - return InvalidOffset(); - if (!Add && Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset)) - return InvalidOffset(); - - // Offset is valid - compute it on unsigned. - int64_t WideIndex = static_cast<int64_t>(Index); - int64_t WideOffset = static_cast<int64_t>(Offset); - int64_t Result = Add ? (WideIndex + WideOffset) : (WideIndex - WideOffset); - S.Stk.push<Pointer>(Ptr.atIndex(static_cast<unsigned>(Result))); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool AddOffset(InterpState &S, CodePtr OpPC) { - return OffsetHelper<T, true>(S, OpPC); -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool SubOffset(InterpState &S, CodePtr OpPC) { - return OffsetHelper<T, false>(S, OpPC); -} - - -//===----------------------------------------------------------------------===// -// Destroy -//===----------------------------------------------------------------------===// - -inline bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I) { - S.Current->destroy(I); - return true; -} - -//===----------------------------------------------------------------------===// -// Cast, CastFP -//===----------------------------------------------------------------------===// - -template <PrimType TIn, PrimType TOut> bool Cast(InterpState &S, CodePtr OpPC) { - using T = typename PrimConv<TIn>::T; - using U = typename PrimConv<TOut>::T; - S.Stk.push<U>(U::from(S.Stk.pop<T>())); - return true; -} - -//===----------------------------------------------------------------------===// -// Zero, Nullptr -//===----------------------------------------------------------------------===// - -template <PrimType Name, class T = typename PrimConv<Name>::T> -bool Zero(InterpState &S, CodePtr OpPC) { - S.Stk.push<T>(T::zero()); - return true; -} - -template <PrimType Name, class T = typename PrimConv<Name>::T> -inline bool Null(InterpState &S, CodePtr OpPC) { - S.Stk.push<T>(); - return true; -} - -//===----------------------------------------------------------------------===// -// This, ImplicitThis -//===----------------------------------------------------------------------===// - -inline bool This(InterpState &S, CodePtr OpPC) { - // Cannot read 'this' in this mode. - if (S.checkingPotentialConstantExpression()) { - return false; - } - - const Pointer &This = S.Current->getThis(); - if (!CheckThis(S, OpPC, This)) - return false; - - S.Stk.push<Pointer>(This); - return true; -} - -//===----------------------------------------------------------------------===// -// Shr, Shl -//===----------------------------------------------------------------------===// - -template <PrimType TR, PrimType TL, class T = typename PrimConv<TR>::T> -unsigned Trunc(InterpState &S, CodePtr OpPC, unsigned Bits, const T &V) { - // C++11 [expr.shift]p1: Shift width must be less than the bit width of - // the shifted type. - if (Bits > 1 && V >= T::from(Bits, V.bitWidth())) { - const Expr *E = S.Current->getExpr(OpPC); - const APSInt Val = V.toAPSInt(); - QualType Ty = E->getType(); - S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits; - return Bits; - } else { - return static_cast<unsigned>(V); - } -} - -template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T> -inline bool ShiftRight(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) { - if (RHS >= V.bitWidth()) { - S.Stk.push<T>(T::from(0, V.bitWidth())); - } else { - S.Stk.push<T>(T::from(V >> RHS, V.bitWidth())); - } - return true; -} - -template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T> -inline bool ShiftLeft(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) { - if (V.isSigned() && !S.getLangOpts().CPlusPlus2a) { - // C++11 [expr.shift]p2: A signed left shift must have a non-negative - // operand, and must not overflow the corresponding unsigned type. - // C++2a [expr.shift]p2: E1 << E2 is the unique value congruent to - // E1 x 2^E2 module 2^N. - if (V.isNegative()) { - const Expr *E = S.Current->getExpr(OpPC); - S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << V.toAPSInt(); - } else if (V.countLeadingZeros() < RHS) { - S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards); - } - } - - if (V.bitWidth() == 1) { - S.Stk.push<T>(V); - } else if (RHS >= V.bitWidth()) { - S.Stk.push<T>(T::from(0, V.bitWidth())); - } else { - S.Stk.push<T>(T::from(V.toUnsigned() << RHS, V.bitWidth())); - } - return true; -} - -template <PrimType TL, PrimType TR> -inline bool Shr(InterpState &S, CodePtr OpPC) { - const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>(); - const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>(); - const unsigned Bits = LHS.bitWidth(); - - if (RHS.isSigned() && RHS.isNegative()) { - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); - return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS)); - } else { - return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS)); - } -} - -template <PrimType TL, PrimType TR> -inline bool Shl(InterpState &S, CodePtr OpPC) { - const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>(); - const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>(); - const unsigned Bits = LHS.bitWidth(); - - if (RHS.isSigned() && RHS.isNegative()) { - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); - return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS)); - } else { - return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS)); - } -} - -//===----------------------------------------------------------------------===// -// NoRet -//===----------------------------------------------------------------------===// - -inline bool NoRet(InterpState &S, CodePtr OpPC) { - SourceLocation EndLoc = S.Current->getCallee()->getEndLoc(); - S.FFDiag(EndLoc, diag::note_constexpr_no_return); - return false; -} - -//===----------------------------------------------------------------------===// -// NarrowPtr, ExpandPtr -//===----------------------------------------------------------------------===// - -inline bool NarrowPtr(InterpState &S, CodePtr OpPC) { - const Pointer &Ptr = S.Stk.pop<Pointer>(); - S.Stk.push<Pointer>(Ptr.narrow()); - return true; -} - -inline bool ExpandPtr(InterpState &S, CodePtr OpPC) { - const Pointer &Ptr = S.Stk.pop<Pointer>(); - S.Stk.push<Pointer>(Ptr.expand()); - return true; -} - -/// Interpreter entry point. -bool Interpret(InterpState &S, APValue &Result); - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/InterpFrame.cpp b/clang/lib/AST/Interp/InterpFrame.cpp deleted file mode 100644 index b787294ee09..00000000000 --- a/clang/lib/AST/Interp/InterpFrame.cpp +++ /dev/null @@ -1,193 +0,0 @@ -//===--- InterpFrame.cpp - Call Frame implementation for the VM -*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "InterpFrame.h" -#include "Function.h" -#include "Interp.h" -#include "InterpStack.h" -#include "Program.h" -#include "Type.h" -#include "clang/AST/DeclCXX.h" - -using namespace clang; -using namespace clang::interp; - -InterpFrame::InterpFrame(InterpState &S, Function *Func, InterpFrame *Caller, - CodePtr RetPC, Pointer &&This) - : Caller(Caller), S(S), Func(Func), This(std::move(This)), RetPC(RetPC), - ArgSize(Func ? Func->getArgSize() : 0), - Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) { - if (Func) { - if (unsigned FrameSize = Func->getFrameSize()) { - Locals = std::make_unique<char[]>(FrameSize); - for (auto &Scope : Func->scopes()) { - for (auto &Local : Scope.locals()) { - Block *B = new (localBlock(Local.Offset)) Block(Local.Desc); - B->invokeCtor(); - } - } - } - } -} - -InterpFrame::~InterpFrame() { - if (Func && Func->isConstructor() && This.isBaseClass()) - This.initialize(); - for (auto &Param : Params) - S.deallocate(reinterpret_cast<Block *>(Param.second.get())); -} - -void InterpFrame::destroy(unsigned Idx) { - for (auto &Local : Func->getScope(Idx).locals()) { - S.deallocate(reinterpret_cast<Block *>(localBlock(Local.Offset))); - } -} - -void InterpFrame::popArgs() { - for (PrimType Ty : Func->args_reverse()) - TYPE_SWITCH(Ty, S.Stk.discard<T>()); -} - -template <typename T> -static void print(llvm::raw_ostream &OS, const T &V, ASTContext &, QualType) { - OS << V; -} - -template <> -void print(llvm::raw_ostream &OS, const Pointer &P, ASTContext &Ctx, - QualType Ty) { - if (P.isZero()) { - OS << "nullptr"; - return; - } - - auto printDesc = [&OS, &Ctx](Descriptor *Desc) { - if (auto *D = Desc->asDecl()) { - // Subfields or named values. - if (auto *VD = dyn_cast<ValueDecl>(D)) { - OS << *VD; - return; - } - // Base classes. - if (isa<RecordDecl>(D)) { - return; - } - } - // Temporary expression. - if (auto *E = Desc->asExpr()) { - E->printPretty(OS, nullptr, Ctx.getPrintingPolicy()); - return; - } - llvm_unreachable("Invalid descriptor type"); - }; - - if (!Ty->isReferenceType()) - OS << "&"; - llvm::SmallVector<Pointer, 2> Levels; - for (Pointer F = P; !F.isRoot(); ) { - Levels.push_back(F); - F = F.isArrayElement() ? F.getArray().expand() : F.getBase(); - } - - printDesc(P.getDeclDesc()); - for (auto It = Levels.rbegin(); It != Levels.rend(); ++It) { - if (It->inArray()) { - OS << "[" << It->expand().getIndex() << "]"; - continue; - } - if (auto Index = It->getIndex()) { - OS << " + " << Index; - continue; - } - OS << "."; - printDesc(It->getFieldDesc()); - } -} - -void InterpFrame::describe(llvm::raw_ostream &OS) { - const FunctionDecl *F = getCallee(); - auto *M = dyn_cast<CXXMethodDecl>(F); - if (M && M->isInstance() && !isa<CXXConstructorDecl>(F)) { - print(OS, This, S.getCtx(), S.getCtx().getRecordType(M->getParent())); - OS << "->"; - } - OS << *F << "("; - unsigned Off = Func->hasRVO() ? primSize(PT_Ptr) : 0; - for (unsigned I = 0, N = F->getNumParams(); I < N; ++I) { - QualType Ty = F->getParamDecl(I)->getType(); - - PrimType PrimTy; - if (llvm::Optional<PrimType> T = S.Ctx.classify(Ty)) { - PrimTy = *T; - } else { - PrimTy = PT_Ptr; - } - - TYPE_SWITCH(PrimTy, print(OS, stackRef<T>(Off), S.getCtx(), Ty)); - Off += align(primSize(PrimTy)); - if (I + 1 != N) - OS << ", "; - } - OS << ")"; -} - -Frame *InterpFrame::getCaller() const { - if (Caller->Caller) - return Caller; - return S.getSplitFrame(); -} - -SourceLocation InterpFrame::getCallLocation() const { - if (!Caller->Func) - return S.getLocation(nullptr, {}); - return S.getLocation(Caller->Func, RetPC - sizeof(uintptr_t)); -} - -const FunctionDecl *InterpFrame::getCallee() const { - return Func->getDecl(); -} - -Pointer InterpFrame::getLocalPointer(unsigned Offset) { - assert(Offset < Func->getFrameSize() && "Invalid local offset."); - return Pointer( - reinterpret_cast<Block *>(Locals.get() + Offset - sizeof(Block))); -} - -Pointer InterpFrame::getParamPointer(unsigned Off) { - // Return the block if it was created previously. - auto Pt = Params.find(Off); - if (Pt != Params.end()) { - return Pointer(reinterpret_cast<Block *>(Pt->second.get())); - } - - // Allocate memory to store the parameter and the block metadata. - const auto &Desc = Func->getParamDescriptor(Off); - size_t BlockSize = sizeof(Block) + Desc.second->getAllocSize(); - auto Memory = std::make_unique<char[]>(BlockSize); - auto *B = new (Memory.get()) Block(Desc.second); - - // Copy the initial value. - TYPE_SWITCH(Desc.first, new (B->data()) T(stackRef<T>(Off))); - - // Record the param. - Params.insert({Off, std::move(Memory)}); - return Pointer(B); -} - -SourceInfo InterpFrame::getSource(CodePtr PC) const { - return S.getSource(Func, PC); -} - -const Expr *InterpFrame::getExpr(CodePtr PC) const { - return S.getExpr(Func, PC); -} - -SourceLocation InterpFrame::getLocation(CodePtr PC) const { - return S.getLocation(Func, PC); -} - diff --git a/clang/lib/AST/Interp/InterpFrame.h b/clang/lib/AST/Interp/InterpFrame.h deleted file mode 100644 index b8391b0bcf9..00000000000 --- a/clang/lib/AST/Interp/InterpFrame.h +++ /dev/null @@ -1,153 +0,0 @@ -//===--- InterpFrame.h - Call Frame implementation for the VM ---*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the class storing information about stack frames in the interpreter. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_INTERPFRAME_H -#define LLVM_CLANG_AST_INTERP_INTERPFRAME_H - -#include "Frame.h" -#include "Pointer.h" -#include "Program.h" -#include "State.h" -#include <cstdint> -#include <vector> - -namespace clang { -namespace interp { -class Function; -class InterpState; - -/// Frame storing local variables. -class InterpFrame final : public Frame { -public: - /// The frame of the previous function. - InterpFrame *Caller; - - /// Creates a new frame for a method call. - InterpFrame(InterpState &S, Function *Func, InterpFrame *Caller, - CodePtr RetPC, Pointer &&This); - - /// Destroys the frame, killing all live pointers to stack slots. - ~InterpFrame(); - - /// Invokes the destructors for a scope. - void destroy(unsigned Idx); - - /// Pops the arguments off the stack. - void popArgs(); - - /// Describes the frame with arguments for diagnostic purposes. - void describe(llvm::raw_ostream &OS); - - /// Returns the parent frame object. - Frame *getCaller() const; - - /// Returns the location of the call to the frame. - SourceLocation getCallLocation() const; - - /// Returns the caller. - const FunctionDecl *getCallee() const; - - /// Returns the current function. - Function *getFunction() const { return Func; } - - /// Returns the offset on the stack at which the frame starts. - size_t getFrameOffset() const { return FrameOffset; } - - /// Returns the value of a local variable. - template <typename T> const T &getLocal(unsigned Offset) { - return localRef<T>(Offset); - } - - /// Mutates a local variable. - template <typename T> void setLocal(unsigned Offset, const T &Value) { - localRef<T>(Offset) = Value; - } - - /// Returns a pointer to a local variables. - Pointer getLocalPointer(unsigned Offset); - - /// Returns the value of an argument. - template <typename T> const T &getParam(unsigned Offset) { - auto Pt = Params.find(Offset); - if (Pt == Params.end()) { - return stackRef<T>(Offset); - } else { - return Pointer(reinterpret_cast<Block *>(Pt->second.get())).deref<T>(); - } - } - - /// Mutates a local copy of a parameter. - template <typename T> void setParam(unsigned Offset, const T &Value) { - getParamPointer(Offset).deref<T>() = Value; - } - - /// Returns a pointer to an argument - lazily creates a block. - Pointer getParamPointer(unsigned Offset); - - /// Returns the 'this' pointer. - const Pointer &getThis() const { return This; } - - /// Checks if the frame is a root frame - return should quit the interpreter. - bool isRoot() const { return !Func; } - - /// Returns the PC of the frame's code start. - CodePtr getPC() const { return Func->getCodeBegin(); } - - /// Returns the return address of the frame. - CodePtr getRetPC() const { return RetPC; } - - /// Map a location to a source. - virtual SourceInfo getSource(CodePtr PC) const; - const Expr *getExpr(CodePtr PC) const; - SourceLocation getLocation(CodePtr PC) const; - -private: - /// Returns an original argument from the stack. - template <typename T> const T &stackRef(unsigned Offset) { - return *reinterpret_cast<const T *>(Args - ArgSize + Offset); - } - - /// Returns an offset to a local. - template <typename T> T &localRef(unsigned Offset) { - return *reinterpret_cast<T *>(Locals.get() + Offset); - } - - /// Returns a pointer to a local's block. - void *localBlock(unsigned Offset) { - return Locals.get() + Offset - sizeof(Block); - } - -private: - /// Reference to the interpreter state. - InterpState &S; - /// Reference to the function being executed. - Function *Func; - /// Current object pointer for methods. - Pointer This; - /// Return address. - CodePtr RetPC; - /// The size of all the arguments. - const unsigned ArgSize; - /// Pointer to the arguments in the callee's frame. - char *Args = nullptr; - /// Fixed, initial storage for known local variables. - std::unique_ptr<char[]> Locals; - /// Offset on the stack at entry. - const size_t FrameOffset; - /// Mapping from arg offsets to their argument blocks. - llvm::DenseMap<unsigned, std::unique_ptr<char[]>> Params; -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/InterpStack.cpp b/clang/lib/AST/Interp/InterpStack.cpp deleted file mode 100644 index f159fe1955f..00000000000 --- a/clang/lib/AST/Interp/InterpStack.cpp +++ /dev/null @@ -1,77 +0,0 @@ -//===--- InterpStack.cpp - Stack implementation for the VM ------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include <cassert> -#include "InterpStack.h" - -using namespace clang; -using namespace clang::interp; - -InterpStack::~InterpStack() { - clear(); -} - -void InterpStack::clear() { - if (Chunk && Chunk->Next) - free(Chunk->Next); - if (Chunk) - free(Chunk); - Chunk = nullptr; - StackSize = 0; -} - -void *InterpStack::grow(size_t Size) { - assert(Size < ChunkSize - sizeof(StackChunk) && "Object too large"); - - if (!Chunk || sizeof(StackChunk) + Chunk->size() + Size > ChunkSize) { - if (Chunk && Chunk->Next) { - Chunk = Chunk->Next; - } else { - StackChunk *Next = new (malloc(ChunkSize)) StackChunk(Chunk); - if (Chunk) - Chunk->Next = Next; - Chunk = Next; - } - } - - auto *Object = reinterpret_cast<void *>(Chunk->End); - Chunk->End += Size; - StackSize += Size; - return Object; -} - -void *InterpStack::peek(size_t Size) { - assert(Chunk && "Stack is empty!"); - - StackChunk *Ptr = Chunk; - while (Size > Ptr->size()) { - Size -= Ptr->size(); - Ptr = Ptr->Prev; - assert(Ptr && "Offset too large"); - } - - return reinterpret_cast<void *>(Ptr->End - Size); -} - -void InterpStack::shrink(size_t Size) { - assert(Chunk && "Chunk is empty!"); - - while (Size > Chunk->size()) { - Size -= Chunk->size(); - if (Chunk->Next) { - free(Chunk->Next); - Chunk->Next = nullptr; - } - Chunk->End = Chunk->start(); - Chunk = Chunk->Prev; - assert(Chunk && "Offset too large"); - } - - Chunk->End -= Size; - StackSize -= Size; -} diff --git a/clang/lib/AST/Interp/InterpStack.h b/clang/lib/AST/Interp/InterpStack.h deleted file mode 100644 index 7ae09a23370..00000000000 --- a/clang/lib/AST/Interp/InterpStack.h +++ /dev/null @@ -1,113 +0,0 @@ -//===--- InterpStack.h - Stack implementation for the VM --------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the upwards-growing stack used by the interpreter. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_INTERPSTACK_H -#define LLVM_CLANG_AST_INTERP_INTERPSTACK_H - -#include <memory> - -namespace clang { -namespace interp { - -/// Stack frame storing temporaries and parameters. -class InterpStack final { -public: - InterpStack() {} - - /// Destroys the stack, freeing up storage. - ~InterpStack(); - - /// Constructs a value in place on the top of the stack. - template <typename T, typename... Tys> void push(Tys &&... Args) { - new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...); - } - - /// Returns the value from the top of the stack and removes it. - template <typename T> T pop() { - auto *Ptr = &peek<T>(); - auto Value = std::move(*Ptr); - Ptr->~T(); - shrink(aligned_size<T>()); - return Value; - } - - /// Discards the top value from the stack. - template <typename T> void discard() { - auto *Ptr = &peek<T>(); - Ptr->~T(); - shrink(aligned_size<T>()); - } - - /// Returns a reference to the value on the top of the stack. - template <typename T> T &peek() { - return *reinterpret_cast<T *>(peek(aligned_size<T>())); - } - - /// Returns a pointer to the top object. - void *top() { return Chunk ? peek(0) : nullptr; } - - /// Returns the size of the stack in bytes. - size_t size() const { return StackSize; } - - /// Clears the stack without calling any destructors. - void clear(); - -private: - /// All stack slots are aligned to the native pointer alignment for storage. - template <typename T> constexpr size_t aligned_size() const { - static_assert(alignof(void *) % alignof(T) == 0, "invalid alignment"); - return ((sizeof(T) + alignof(void *) - 1) / alignof(void *)) * - alignof(void *); - } - - /// Grows the stack to accomodate a value and returns a pointer to it. - void *grow(size_t Size); - /// Returns a pointer from the top of the stack. - void *peek(size_t Size); - /// Shrinks the stack. - void shrink(size_t Size); - - /// Allocate stack space in 1Mb chunks. - static constexpr size_t ChunkSize = 1024 * 1024; - - /// Metadata for each stack chunk. - /// - /// The stack is composed of a linked list of chunks. Whenever an allocation - /// is out of bounds, a new chunk is linked. When a chunk becomes empty, - /// it is not immediately freed: a chunk is deallocated only when the - /// predecessor becomes empty. - struct StackChunk { - StackChunk *Next; - StackChunk *Prev; - char *End; - - StackChunk(StackChunk *Prev = nullptr) - : Next(nullptr), Prev(Prev), End(reinterpret_cast<char *>(this + 1)) {} - - /// Returns the size of the chunk, minus the header. - size_t size() { return End - start(); } - - /// Returns a pointer to the start of the data region. - char *start() { return reinterpret_cast<char *>(this + 1); } - }; - static_assert(sizeof(StackChunk) < ChunkSize, "Invalid chunk size"); - - /// First chunk on the stack. - StackChunk *Chunk = nullptr; - /// Total size of the stack. - size_t StackSize = 0; -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/InterpState.cpp b/clang/lib/AST/Interp/InterpState.cpp deleted file mode 100644 index 45476a05e2e..00000000000 --- a/clang/lib/AST/Interp/InterpState.cpp +++ /dev/null @@ -1,74 +0,0 @@ -//===--- InterpState.cpp - Interpreter for the constexpr VM -----*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "InterpState.h" -#include "Function.h" -#include "InterpFrame.h" -#include "InterpStack.h" -#include "Opcode.h" -#include "Program.h" -#include "State.h" -#include "Type.h" -#include <limits> - -using namespace clang; -using namespace clang::interp; - -using APSInt = llvm::APSInt; - -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) {} - -InterpState::~InterpState() { - while (Current) { - InterpFrame *Next = Current->Caller; - delete Current; - Current = Next; - } - - while (DeadBlocks) { - DeadBlock *Next = DeadBlocks->Next; - free(DeadBlocks); - DeadBlocks = Next; - } -} - -Frame *InterpState::getCurrentFrame() { - if (Current && Current->Caller) { - return Current; - } else { - return Parent.getCurrentFrame(); - } -} - -bool InterpState::reportOverflow(const Expr *E, const llvm::APSInt &Value) { - QualType Type = E->getType(); - CCEDiag(E, diag::note_constexpr_overflow) << Value << Type; - return noteUndefinedBehavior(); -} - -void InterpState::deallocate(Block *B) { - Descriptor *Desc = B->getDescriptor(); - if (B->hasPointers()) { - size_t Size = B->getSize(); - - // Allocate a new block, transferring over pointers. - char *Memory = reinterpret_cast<char *>(malloc(sizeof(DeadBlock) + Size)); - auto *D = new (Memory) DeadBlock(DeadBlocks, B); - - // Move data from one block to another. - if (Desc->MoveFn) - Desc->MoveFn(B, B->data(), D->data(), Desc); - } else { - // Free storage, if necessary. - if (Desc->DtorFn) - Desc->DtorFn(B, B->data(), Desc); - } -} diff --git a/clang/lib/AST/Interp/InterpState.h b/clang/lib/AST/Interp/InterpState.h deleted file mode 100644 index fe4042ac207..00000000000 --- a/clang/lib/AST/Interp/InterpState.h +++ /dev/null @@ -1,112 +0,0 @@ -//===--- InterpState.h - Interpreter state for the constexpr VM -*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Definition of the interpreter state and entry point. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_INTERPSTATE_H -#define LLVM_CLANG_AST_INTERP_INTERPSTATE_H - -#include "Context.h" -#include "Function.h" -#include "InterpStack.h" -#include "State.h" -#include "clang/AST/APValue.h" -#include "clang/AST/ASTDiagnostic.h" -#include "clang/AST/Expr.h" -#include "clang/Basic/OptionalDiagnostic.h" - -namespace clang { -namespace interp { -class Context; -class Function; -class InterpStack; -class InterpFrame; -class SourceMapper; - -/// Interpreter context. -class InterpState final : public State, public SourceMapper { -public: - InterpState(State &Parent, Program &P, InterpStack &Stk, Context &Ctx, - SourceMapper *M = nullptr); - - ~InterpState(); - - // Stack frame accessors. - Frame *getSplitFrame() { return Parent.getCurrentFrame(); } - Frame *getCurrentFrame() override; - unsigned getCallStackDepth() override { return CallStackDepth; } - const Frame *getBottomFrame() const override { - return Parent.getBottomFrame(); - } - - // Acces objects from the walker context. - Expr::EvalStatus &getEvalStatus() const override { - return Parent.getEvalStatus(); - } - ASTContext &getCtx() const override { return Parent.getCtx(); } - - // Forward status checks and updates to the walker. - bool checkingForOverflow() const override { - return Parent.checkingForOverflow(); - } - bool keepEvaluatingAfterFailure() const override { - return Parent.keepEvaluatingAfterFailure(); - } - bool checkingPotentialConstantExpression() const override { - return Parent.checkingPotentialConstantExpression(); - } - bool noteUndefinedBehavior() override { - return Parent.noteUndefinedBehavior(); - } - bool hasActiveDiagnostic() override { return Parent.hasActiveDiagnostic(); } - void setActiveDiagnostic(bool Flag) override { - Parent.setActiveDiagnostic(Flag); - } - void setFoldFailureDiagnostic(bool Flag) override { - Parent.setFoldFailureDiagnostic(Flag); - } - bool hasPriorDiagnostic() override { return Parent.hasPriorDiagnostic(); } - - /// Reports overflow and return true if evaluation should continue. - bool reportOverflow(const Expr *E, const llvm::APSInt &Value); - - /// Deallocates a pointer. - void deallocate(Block *B); - - /// Delegates source mapping to the mapper. - SourceInfo getSource(Function *F, CodePtr PC) const override { - return M ? M->getSource(F, PC) : F->getSource(PC); - } - -private: - /// AST Walker state. - State &Parent; - /// Dead block chain. - DeadBlock *DeadBlocks = nullptr; - /// Reference to the offset-source mapping. - SourceMapper *M; - -public: - /// Reference to the module containing all bytecode. - Program &P; - /// Temporary stack. - InterpStack &Stk; - /// Interpreter Context. - Context &Ctx; - /// The current frame. - InterpFrame *Current = nullptr; - /// Call stack depth. - unsigned CallStackDepth; -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/Opcode.h b/clang/lib/AST/Interp/Opcode.h deleted file mode 100644 index d2daa1ea52a..00000000000 --- a/clang/lib/AST/Interp/Opcode.h +++ /dev/null @@ -1,30 +0,0 @@ -//===--- Opcode.h - Opcodes for the constexpr VM ----------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines all opcodes executed by the VM and emitted by the compiler. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_OPCODE_H -#define LLVM_CLANG_AST_INTERP_OPCODE_H - -#include <cstdint> - -namespace clang { -namespace interp { - -enum Opcode : uint32_t { -#define GET_OPCODE_NAMES -#include "Opcodes.inc" -#undef GET_OPCODE_NAMES -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td deleted file mode 100644 index 4aba5f5cd83..00000000000 --- a/clang/lib/AST/Interp/Opcodes.td +++ /dev/null @@ -1,422 +0,0 @@ -//===--- Opcodes.td - Opcode defitions for the constexpr VM -----*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Helper file used to generate opcodes, the interpreter and the disassembler. -// -//===----------------------------------------------------------------------===// - - -//===----------------------------------------------------------------------===// -// Types evaluated by the interpreter. -//===----------------------------------------------------------------------===// - -class Type; -def Bool : Type; -def Sint8 : Type; -def Uint8 : Type; -def Sint16 : Type; -def Uint16 : Type; -def Sint32 : Type; -def Uint32 : Type; -def Sint64 : Type; -def Uint64 : Type; -def Ptr : Type; - -//===----------------------------------------------------------------------===// -// Types transferred to the interpreter. -//===----------------------------------------------------------------------===// - -class ArgType { string Name = ?; } -def ArgSint8 : ArgType { let Name = "int8_t"; } -def ArgUint8 : ArgType { let Name = "uint8_t"; } -def ArgSint16 : ArgType { let Name = "int16_t"; } -def ArgUint16 : ArgType { let Name = "uint16_t"; } -def ArgSint32 : ArgType { let Name = "int32_t"; } -def ArgUint32 : ArgType { let Name = "uint32_t"; } -def ArgSint64 : ArgType { let Name = "int64_t"; } -def ArgUint64 : ArgType { let Name = "uint64_t"; } -def ArgBool : ArgType { let Name = "bool"; } - -def ArgFunction : ArgType { let Name = "Function *"; } -def ArgRecord : ArgType { let Name = "Record *"; } - -def ArgSema : ArgType { let Name = "const fltSemantics *"; } - -def ArgExpr : ArgType { let Name = "const Expr *"; } -def ArgFloatingLiteral : ArgType { let Name = "const FloatingLiteral *"; } -def ArgCXXMethodDecl : ArgType { let Name = "const CXXMethodDecl *"; } -def ArgFunctionDecl : ArgType { let Name = "const FunctionDecl *"; } -def ArgRecordDecl : ArgType { let Name = "const RecordDecl *"; } -def ArgCXXRecordDecl : ArgType { let Name = "const CXXRecordDecl *"; } -def ArgValueDecl : ArgType { let Name = "const ValueDecl *"; } -def ArgRecordField : ArgType { let Name = "const Record::Field *"; } - -//===----------------------------------------------------------------------===// -// Classes of types intructions operate on. -//===----------------------------------------------------------------------===// - -class TypeClass { - list<Type> Types; -} - -def AluTypeClass : TypeClass { - let Types = [Sint8, Uint8, Sint16, Uint16, Sint32, - Uint32, Sint64, Uint64, Bool]; -} - -def PtrTypeClass : TypeClass { - let Types = [Ptr]; -} - -def AllTypeClass : TypeClass { - let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types); -} - -def ComparableTypeClass : TypeClass { - let Types = !listconcat(AluTypeClass.Types, [Ptr]); -} - -class SingletonTypeClass<Type Ty> : TypeClass { - let Types = [Ty]; -} - -//===----------------------------------------------------------------------===// -// Record describing all opcodes. -//===----------------------------------------------------------------------===// - -class Opcode { - list<TypeClass> Types = []; - list<ArgType> Args = []; - string Name = ""; - bit CanReturn = 0; - bit ChangesPC = 0; - bit HasCustomLink = 0; - bit HasCustomEval = 0; - bit HasGroup = 0; -} - -class AluOpcode : Opcode { - let Types = [AluTypeClass]; - let HasGroup = 1; -} - -//===----------------------------------------------------------------------===// -// Jump opcodes -//===----------------------------------------------------------------------===// - -class JumpOpcode : Opcode { - let Args = [ArgSint32]; - let ChangesPC = 1; - let HasCustomEval = 1; -} - -// [] -> [] -def Jmp : JumpOpcode; -// [Bool] -> [], jumps if true. -def Jt : JumpOpcode; -// [Bool] -> [], jumps if false. -def Jf : JumpOpcode; - -//===----------------------------------------------------------------------===// -// Returns -//===----------------------------------------------------------------------===// - -// [Value] -> [] -def Ret : Opcode { - let Types = [AllTypeClass]; - let ChangesPC = 1; - let CanReturn = 1; - let HasGroup = 1; - let HasCustomEval = 1; -} -// [] -> [] -def RetVoid : Opcode { - let CanReturn = 1; - let ChangesPC = 1; - let HasCustomEval = 1; -} -// [Value] -> [] -def RetValue : Opcode { - let CanReturn = 1; - let ChangesPC = 1; - let HasCustomEval = 1; -} -// [] -> EXIT -def NoRet : Opcode {} - -//===----------------------------------------------------------------------===// -// Frame management -//===----------------------------------------------------------------------===// - -// [] -> [] -def Destroy : Opcode { - let Args = [ArgUint32]; - let HasCustomEval = 1; -} - -//===----------------------------------------------------------------------===// -// Constants -//===----------------------------------------------------------------------===// - -class ConstOpcode<Type Ty, ArgType ArgTy> : Opcode { - let Types = [SingletonTypeClass<Ty>]; - let Args = [ArgTy]; - let Name = "Const"; -} - -// [] -> [Integer] -def ConstSint8 : ConstOpcode<Sint8, ArgSint8>; -def ConstUint8 : ConstOpcode<Uint8, ArgUint8>; -def ConstSint16 : ConstOpcode<Sint16, ArgSint16>; -def ConstUint16 : ConstOpcode<Uint16, ArgUint16>; -def ConstSint32 : ConstOpcode<Sint32, ArgSint32>; -def ConstUint32 : ConstOpcode<Uint32, ArgUint32>; -def ConstSint64 : ConstOpcode<Sint64, ArgSint64>; -def ConstUint64 : ConstOpcode<Uint64, ArgUint64>; -def ConstBool : ConstOpcode<Bool, ArgBool>; - -// [] -> [Integer] -def Zero : Opcode { - let Types = [AluTypeClass]; -} - -// [] -> [Pointer] -def Null : Opcode { - let Types = [PtrTypeClass]; -} - -//===----------------------------------------------------------------------===// -// Pointer generation -//===----------------------------------------------------------------------===// - -// [] -> [Pointer] -def GetPtrLocal : Opcode { - // Offset of local. - let Args = [ArgUint32]; - bit HasCustomEval = 1; -} -// [] -> [Pointer] -def GetPtrParam : Opcode { - // Offset of parameter. - let Args = [ArgUint32]; -} -// [] -> [Pointer] -def GetPtrGlobal : Opcode { - // Index of global. - let Args = [ArgUint32]; -} -// [Pointer] -> [Pointer] -def GetPtrField : Opcode { - // Offset of field. - let Args = [ArgUint32]; -} -// [Pointer] -> [Pointer] -def GetPtrActiveField : Opcode { - // Offset of field. - let Args = [ArgUint32]; -} -// [] -> [Pointer] -def GetPtrActiveThisField : Opcode { - // Offset of field. - let Args = [ArgUint32]; -} -// [] -> [Pointer] -def GetPtrThisField : Opcode { - // Offset of field. - let Args = [ArgUint32]; -} -// [Pointer] -> [Pointer] -def GetPtrBase : Opcode { - // Offset of field, which is a base. - let Args = [ArgUint32]; -} -// [Pointer] -> [Pointer] -def GetPtrVirtBase : Opcode { - // RecordDecl of base class. - let Args = [ArgRecordDecl]; -} -// [] -> [Pointer] -def GetPtrThisBase : Opcode { - // Offset of field, which is a base. - let Args = [ArgUint32]; -} -// [] -> [Pointer] -def GetPtrThisVirtBase : Opcode { - // RecordDecl of base class. - let Args = [ArgRecordDecl]; -} -// [] -> [Pointer] -def This : Opcode; - -// [Pointer] -> [Pointer] -def NarrowPtr : Opcode; -// [Pointer] -> [Pointer] -def ExpandPtr : Opcode; - -//===----------------------------------------------------------------------===// -// Direct field accessors -//===----------------------------------------------------------------------===// - -class AccessOpcode : Opcode { - let Types = [AllTypeClass]; - let Args = [ArgUint32]; - let HasGroup = 1; -} - -class BitFieldOpcode : Opcode { - let Types = [AluTypeClass]; - let Args = [ArgRecordField]; - let HasGroup = 1; -} - -// [] -> [Pointer] -def GetLocal : AccessOpcode { let HasCustomEval = 1; } -// [] -> [Pointer] -def SetLocal : AccessOpcode { let HasCustomEval = 1; } - -// [] -> [Value] -def GetGlobal : AccessOpcode; -// [Value] -> [] -def InitGlobal : AccessOpcode; -// [Value] -> [] -def SetGlobal : AccessOpcode; - -// [] -> [Value] -def GetParam : AccessOpcode; -// [Value] -> [] -def SetParam : AccessOpcode; - -// [Pointer] -> [Pointer, Value] -def GetField : AccessOpcode; -// [Pointer] -> [Value] -def GetFieldPop : AccessOpcode; -// [] -> [Value] -def GetThisField : AccessOpcode; - -// [Pointer, Value] -> [Pointer] -def SetField : AccessOpcode; -// [Value] -> [] -def SetThisField : AccessOpcode; - -// [Value] -> [] -def InitThisField : AccessOpcode; -// [Value] -> [] -def InitThisFieldActive : AccessOpcode; -// [Value] -> [] -def InitThisBitField : BitFieldOpcode; -// [Pointer, Value] -> [] -def InitField : AccessOpcode; -// [Pointer, Value] -> [] -def InitBitField : BitFieldOpcode; -// [Pointer, Value] -> [] -def InitFieldActive : AccessOpcode; - -//===----------------------------------------------------------------------===// -// Pointer access -//===----------------------------------------------------------------------===// - -class LoadOpcode : Opcode { - let Types = [AllTypeClass]; - let HasGroup = 1; -} - -// [Pointer] -> [Pointer, Value] -def Load : LoadOpcode {} -// [Pointer] -> [Value] -def LoadPop : LoadOpcode {} - -class StoreOpcode : Opcode { - let Types = [AllTypeClass]; - let HasGroup = 1; -} - -class StoreBitFieldOpcode : Opcode { - let Types = [AluTypeClass]; - let HasGroup = 1; -} - -// [Pointer, Value] -> [Pointer] -def Store : StoreOpcode {} -// [Pointer, Value] -> [] -def StorePop : StoreOpcode {} - -// [Pointer, Value] -> [Pointer] -def StoreBitField : StoreBitFieldOpcode {} -// [Pointer, Value] -> [] -def StoreBitFieldPop : StoreBitFieldOpcode {} - -// [Pointer, Value] -> [] -def InitPop : StoreOpcode {} -// [Pointer, Value] -> [Pointer] -def InitElem : Opcode { - let Types = [AllTypeClass]; - let Args = [ArgUint32]; - let HasGroup = 1; -} -// [Pointer, Value] -> [] -def InitElemPop : Opcode { - let Types = [AllTypeClass]; - let Args = [ArgUint32]; - let HasGroup = 1; -} - -//===----------------------------------------------------------------------===// -// Pointer arithmetic. -//===----------------------------------------------------------------------===// - -// [Pointer, Integral] -> [Pointer] -def AddOffset : AluOpcode; -// [Pointer, Integral] -> [Pointer] -def SubOffset : AluOpcode; - -//===----------------------------------------------------------------------===// -// Binary operators. -//===----------------------------------------------------------------------===// - -// [Real, Real] -> [Real] -def Sub : AluOpcode; -def Add : AluOpcode; -def Mul : AluOpcode; - -//===----------------------------------------------------------------------===// -// Comparison opcodes. -//===----------------------------------------------------------------------===// - -class EqualityOpcode : Opcode { - let Types = [AllTypeClass]; - let HasGroup = 1; -} - -def EQ : EqualityOpcode; -def NE : EqualityOpcode; - -class ComparisonOpcode : Opcode { - let Types = [ComparableTypeClass]; - let HasGroup = 1; -} - -def LT : ComparisonOpcode; -def LE : ComparisonOpcode; -def GT : ComparisonOpcode; -def GE : ComparisonOpcode; - -//===----------------------------------------------------------------------===// -// Stack management. -//===----------------------------------------------------------------------===// - -// [Value] -> [] -def Pop : Opcode { - let Types = [AllTypeClass]; - let HasGroup = 1; -} - -// [Value] -> [Value, Value] -def Dup : Opcode { - let Types = [AllTypeClass]; - let HasGroup = 1; -} diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp deleted file mode 100644 index dc826596b32..00000000000 --- a/clang/lib/AST/Interp/Pointer.cpp +++ /dev/null @@ -1,193 +0,0 @@ -//===--- Pointer.cpp - Types for the constexpr VM ---------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Block.h" -#include "Pointer.h" -#include "Function.h" -#include "Type.h" - -using namespace clang; -using namespace clang::interp; - -Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {} - -Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {} - -Pointer::Pointer(Pointer &&P) - : Pointee(P.Pointee), Base(P.Base), Offset(P.Offset) { - if (Pointee) - Pointee->movePointer(&P, this); -} - -Pointer::Pointer(Block *Pointee, unsigned Base, unsigned Offset) - : Pointee(Pointee), Base(Base), Offset(Offset) { - assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base"); - if (Pointee) - Pointee->addPointer(this); -} - -Pointer::~Pointer() { - if (Pointee) { - Pointee->removePointer(this); - Pointee->cleanup(); - } -} - -void Pointer::operator=(const Pointer &P) { - Block *Old = Pointee; - - if (Pointee) - Pointee->removePointer(this); - - Offset = P.Offset; - Base = P.Base; - - Pointee = P.Pointee; - if (Pointee) - Pointee->addPointer(this); - - if (Old) - Old->cleanup(); -} - -void Pointer::operator=(Pointer &&P) { - Block *Old = Pointee; - - if (Pointee) - Pointee->removePointer(this); - - Offset = P.Offset; - Base = P.Base; - - Pointee = P.Pointee; - if (Pointee) - Pointee->movePointer(&P, this); - - if (Old) - Old->cleanup(); -} - -APValue Pointer::toAPValue() const { - APValue::LValueBase Base; - llvm::SmallVector<APValue::LValuePathEntry, 5> Path; - CharUnits Offset; - bool IsNullPtr; - bool IsOnePastEnd; - - if (isZero()) { - Base = static_cast<const Expr *>(nullptr); - IsNullPtr = true; - IsOnePastEnd = false; - Offset = CharUnits::Zero(); - } else { - // Build the lvalue base from the block. - Descriptor *Desc = getDeclDesc(); - if (auto *VD = Desc->asValueDecl()) - Base = VD; - else if (auto *E = Desc->asExpr()) - Base = E; - else - llvm_unreachable("Invalid allocation type"); - - // Not a null pointer. - IsNullPtr = false; - - if (isUnknownSizeArray()) { - IsOnePastEnd = false; - Offset = CharUnits::Zero(); - } else { - // TODO: compute the offset into the object. - Offset = CharUnits::Zero(); - - // Build the path into the object. - Pointer Ptr = *this; - while (Ptr.isField()) { - if (Ptr.isArrayElement()) { - Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex())); - Ptr = Ptr.getArray(); - } else { - // TODO: figure out if base is virtual - bool IsVirtual = false; - - // Create a path entry for the field. - Descriptor *Desc = Ptr.getFieldDesc(); - if (auto *BaseOrMember = Desc->asDecl()) { - Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual})); - Ptr = Ptr.getBase(); - continue; - } - llvm_unreachable("Invalid field type"); - } - } - - IsOnePastEnd = isOnePastEnd(); - } - } - - return APValue(Base, Offset, Path, IsOnePastEnd, IsNullPtr); -} - -bool Pointer::isInitialized() const { - assert(Pointee && "Cannot check if null pointer was initialized"); - Descriptor *Desc = getFieldDesc(); - if (Desc->isPrimitiveArray()) { - if (Pointee->IsStatic) - return true; - // Primitive array field are stored in a bitset. - InitMap *Map = getInitMap(); - if (!Map) - return false; - if (Map == (InitMap *)-1) - return true; - return Map->isInitialized(getIndex()); - } else { - // Field has its bit in an inline descriptor. - return Base == 0 || getInlineDesc()->IsInitialized; - } -} - -void Pointer::initialize() const { - assert(Pointee && "Cannot initialize null pointer"); - Descriptor *Desc = getFieldDesc(); - if (Desc->isPrimitiveArray()) { - if (!Pointee->IsStatic) { - // Primitive array initializer. - InitMap *&Map = getInitMap(); - if (Map == (InitMap *)-1) - return; - if (Map == nullptr) - Map = InitMap::allocate(Desc->getNumElems()); - if (Map->initialize(getIndex())) { - free(Map); - Map = (InitMap *)-1; - } - } - } else { - // Field has its bit in an inline descriptor. - assert(Base != 0 && "Only composite fields can be initialised"); - getInlineDesc()->IsInitialized = true; - } -} - -void Pointer::activate() const { - // Field has its bit in an inline descriptor. - assert(Base != 0 && "Only composite fields can be initialised"); - getInlineDesc()->IsActive = true; -} - -void Pointer::deactivate() const { - // TODO: this only appears in constructors, so nothing to deactivate. -} - -bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { - return A.Pointee == B.Pointee; -} - -bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) { - return A.Base == B.Base && A.getFieldDesc()->IsArray; -} diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h deleted file mode 100644 index b8fa98e24fa..00000000000 --- a/clang/lib/AST/Interp/Pointer.h +++ /dev/null @@ -1,353 +0,0 @@ -//===--- Pointer.h - Types for the constexpr VM -----------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the classes responsible for pointer tracking. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_POINTER_H -#define LLVM_CLANG_AST_INTERP_POINTER_H - -#include "Block.h" -#include "Descriptor.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ComparisonCategories.h" -#include "llvm/ADT/PointerUnion.h" -#include "llvm/Support/raw_ostream.h" - -namespace clang { -namespace interp { -class Block; -class DeadBlock; -class Context; -class InterpState; -class Pointer; -class Function; -enum PrimType : unsigned; - -/// A pointer to a memory block, live or dead. -/// -/// This object can be allocated into interpreter stack frames. If pointing to -/// a live block, it is a link in the chain of pointers pointing to the block. -class Pointer { -private: - static constexpr unsigned PastEndMark = (unsigned)-1; - static constexpr unsigned RootPtrMark = (unsigned)-1; - -public: - Pointer() {} - Pointer(Block *B); - Pointer(const Pointer &P); - Pointer(Pointer &&P); - ~Pointer(); - - void operator=(const Pointer &P); - void operator=(Pointer &&P); - - /// Converts the pointer to an APValue. - APValue toAPValue() const; - - /// Offsets a pointer inside an array. - Pointer atIndex(unsigned Idx) const { - if (Base == RootPtrMark) - return Pointer(Pointee, RootPtrMark, getDeclDesc()->getSize()); - unsigned Off = Idx * elemSize(); - if (getFieldDesc()->ElemDesc) - Off += sizeof(InlineDescriptor); - else - Off += sizeof(InitMap *); - return Pointer(Pointee, Base, Base + Off); - } - - /// Creates a pointer to a field. - Pointer atField(unsigned Off) const { - unsigned Field = Offset + Off; - return Pointer(Pointee, Field, Field); - } - - /// Restricts the scope of an array element pointer. - Pointer narrow() const { - // Null pointers cannot be narrowed. - if (isZero() || isUnknownSizeArray()) - return *this; - - // Pointer to an array of base types - enter block. - if (Base == RootPtrMark) - return Pointer(Pointee, 0, Offset == 0 ? Offset : PastEndMark); - - // Pointer is one past end - magic offset marks that. - if (isOnePastEnd()) - return Pointer(Pointee, Base, PastEndMark); - - // Primitive arrays are a bit special since they do not have inline - // descriptors. If Offset != Base, then the pointer already points to - // an element and there is nothing to do. Otherwise, the pointer is - // adjusted to the first element of the array. - if (inPrimitiveArray()) { - if (Offset != Base) - return *this; - return Pointer(Pointee, Base, Offset + sizeof(InitMap *)); - } - - // Pointer is to a field or array element - enter it. - if (Offset != Base) - return Pointer(Pointee, Offset, Offset); - - // Enter the first element of an array. - if (!getFieldDesc()->isArray()) - return *this; - - const unsigned NewBase = Base + sizeof(InlineDescriptor); - return Pointer(Pointee, NewBase, NewBase); - } - - /// Expands a pointer to the containing array, undoing narrowing. - Pointer expand() const { - if (isElementPastEnd()) { - // Revert to an outer one-past-end pointer. - unsigned Adjust; - if (inPrimitiveArray()) - Adjust = sizeof(InitMap *); - else - Adjust = sizeof(InlineDescriptor); - return Pointer(Pointee, Base, Base + getSize() + Adjust); - } - - // Do not step out of array elements. - if (Base != Offset) - return *this; - - // If at base, point to an array of base types. - if (Base == 0) - return Pointer(Pointee, RootPtrMark, 0); - - // Step into the containing array, if inside one. - unsigned Next = Base - getInlineDesc()->Offset; - Descriptor *Desc = Next == 0 ? getDeclDesc() : getDescriptor(Next)->Desc; - if (!Desc->IsArray) - return *this; - return Pointer(Pointee, Next, Offset); - } - - /// Checks if the pointer is null. - bool isZero() const { return Pointee == nullptr; } - /// Checks if the pointer is live. - bool isLive() const { return Pointee && !Pointee->IsDead; } - /// Checks if the item is a field in an object. - bool isField() const { return Base != 0 && Base != RootPtrMark; } - - /// Accessor for information about the declaration site. - Descriptor *getDeclDesc() const { return Pointee->Desc; } - SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); } - - /// Returns a pointer to the object of which this pointer is a field. - Pointer getBase() const { - if (Base == RootPtrMark) { - assert(Offset == PastEndMark && "cannot get base of a block"); - return Pointer(Pointee, Base, 0); - } - assert(Offset == Base && "not an inner field"); - unsigned NewBase = Base - getInlineDesc()->Offset; - return Pointer(Pointee, NewBase, NewBase); - } - /// Returns the parent array. - Pointer getArray() const { - if (Base == RootPtrMark) { - assert(Offset != 0 && Offset != PastEndMark && "not an array element"); - return Pointer(Pointee, Base, 0); - } - assert(Offset != Base && "not an array element"); - return Pointer(Pointee, Base, Base); - } - - /// Accessors for information about the innermost field. - Descriptor *getFieldDesc() const { - if (Base == 0 || Base == RootPtrMark) - return getDeclDesc(); - return getInlineDesc()->Desc; - } - - /// Returns the type of the innermost field. - QualType getType() const { return getFieldDesc()->getType(); } - - /// Returns the element size of the innermost field. - size_t elemSize() const { - if (Base == RootPtrMark) - return getDeclDesc()->getSize(); - return getFieldDesc()->getElemSize(); - } - /// Returns the total size of the innermost field. - size_t getSize() const { return getFieldDesc()->getSize(); } - - /// Returns the offset into an array. - unsigned getOffset() const { - assert(Offset != PastEndMark && "invalid offset"); - if (Base == RootPtrMark) - return Offset; - - unsigned Adjust = 0; - if (Offset != Base) { - if (getFieldDesc()->ElemDesc) - Adjust = sizeof(InlineDescriptor); - else - Adjust = sizeof(InitMap *); - } - return Offset - Base - Adjust; - } - - /// Checks if the innermost field is an array. - bool inArray() const { return getFieldDesc()->IsArray; } - /// Checks if the structure is a primitive array. - bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); } - /// Checks if the structure is an array of unknown size. - bool isUnknownSizeArray() const { - return getFieldDesc()->isUnknownSizeArray(); - } - /// Checks if the pointer points to an array. - bool isArrayElement() const { return Base != Offset; } - /// Pointer points directly to a block. - bool isRoot() const { - return (Base == 0 || Base == RootPtrMark) && Offset == 0; - } - - /// Returns the record descriptor of a class. - Record *getRecord() const { return getFieldDesc()->ElemRecord; } - /// Returns the field information. - const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); } - - /// Checks if the object is a union. - bool isUnion() const; - - /// Checks if the storage is extern. - bool isExtern() const { return Pointee->isExtern(); } - /// Checks if the storage is static. - bool isStatic() const { return Pointee->isStatic(); } - /// Checks if the storage is temporary. - bool isTemporary() const { return Pointee->isTemporary(); } - /// Checks if the storage is a static temporary. - bool isStaticTemporary() const { return isStatic() && isTemporary(); } - - /// Checks if the field is mutable. - bool isMutable() const { return Base != 0 && getInlineDesc()->IsMutable; } - /// Checks if an object was initialized. - bool isInitialized() const; - /// Checks if the object is active. - bool isActive() const { return Base == 0 || getInlineDesc()->IsActive; } - /// Checks if a structure is a base class. - bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; } - - /// Checks if an object or a subfield is mutable. - bool isConst() const { - return Base == 0 ? getDeclDesc()->IsConst : getInlineDesc()->IsConst; - } - - /// Returns the declaration ID. - llvm::Optional<unsigned> getDeclID() const { return Pointee->getDeclID(); } - - /// Returns the byte offset from the start. - unsigned getByteOffset() const { - return Offset; - } - - /// Returns the number of elements. - unsigned getNumElems() const { return getSize() / elemSize(); } - - /// Returns the index into an array. - int64_t getIndex() const { - if (isElementPastEnd()) - return 1; - if (auto ElemSize = elemSize()) - return getOffset() / ElemSize; - return 0; - } - - /// Checks if the index is one past end. - bool isOnePastEnd() const { - return isElementPastEnd() || getSize() == getOffset(); - } - - /// Checks if the pointer is an out-of-bounds element pointer. - bool isElementPastEnd() const { return Offset == PastEndMark; } - - /// Dereferences the pointer, if it's live. - template <typename T> T &deref() const { - assert(isLive() && "Invalid pointer"); - return *reinterpret_cast<T *>(Pointee->data() + Offset); - } - - /// Dereferences a primitive element. - template <typename T> T &elem(unsigned I) const { - return reinterpret_cast<T *>(Pointee->data())[I]; - } - - /// Initializes a field. - void initialize() const; - /// Activats a field. - void activate() const; - /// Deactivates an entire strurcutre. - void deactivate() const; - - /// Checks if two pointers are comparable. - static bool hasSameBase(const Pointer &A, const Pointer &B); - /// Checks if two pointers can be subtracted. - static bool hasSameArray(const Pointer &A, const Pointer &B); - - /// Prints the pointer. - void print(llvm::raw_ostream &OS) const { - OS << "{" << Base << ", " << Offset << ", "; - if (Pointee) - OS << Pointee->getSize(); - else - OS << "nullptr"; - OS << "}"; - } - -private: - friend class Block; - friend class DeadBlock; - - Pointer(Block *Pointee, unsigned Base, unsigned Offset); - - /// Returns the embedded descriptor preceding a field. - InlineDescriptor *getInlineDesc() const { return getDescriptor(Base); } - - /// Returns a descriptor at a given offset. - InlineDescriptor *getDescriptor(unsigned Offset) const { - assert(Offset != 0 && "Not a nested pointer"); - return reinterpret_cast<InlineDescriptor *>(Pointee->data() + Offset) - 1; - } - - /// Returns a reference to the pointer which stores the initialization map. - InitMap *&getInitMap() const { - return *reinterpret_cast<InitMap **>(Pointee->data() + Base); - } - - /// The block the pointer is pointing to. - Block *Pointee = nullptr; - /// Start of the current subfield. - unsigned Base = 0; - /// Offset into the block. - unsigned Offset = 0; - - /// Previous link in the pointer chain. - Pointer *Prev = nullptr; - /// Next link in the pointer chain. - Pointer *Next = nullptr; -}; - -inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) { - P.print(OS); - return OS; -} - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/Program.cpp b/clang/lib/AST/Interp/Program.cpp deleted file mode 100644 index c013757bd76..00000000000 --- a/clang/lib/AST/Interp/Program.cpp +++ /dev/null @@ -1,364 +0,0 @@ -//===--- Program.cpp - Bytecode for the constexpr VM ------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Program.h" -#include "ByteCodeStmtGen.h" -#include "Context.h" -#include "Function.h" -#include "Opcode.h" -#include "Type.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclCXX.h" - -using namespace clang; -using namespace clang::interp; - -unsigned Program::createGlobalString(const StringLiteral *S) { - const size_t CharWidth = S->getCharByteWidth(); - const size_t BitWidth = CharWidth * Ctx.getCharBit(); - - PrimType CharType; - switch (CharWidth) { - case 1: - CharType = PT_Sint8; - break; - case 2: - CharType = PT_Uint16; - break; - case 4: - CharType = PT_Uint32; - break; - default: - llvm_unreachable("unsupported character width"); - } - - // Create a descriptor for the string. - Descriptor *Desc = allocateDescriptor(S, CharType, S->getLength() + 1, - /*isConst=*/true, - /*isTemporary=*/false, - /*isMutable=*/false); - - // Allocate storage for the string. - // The byte length does not include the null terminator. - unsigned I = Globals.size(); - unsigned Sz = Desc->getAllocSize(); - auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true, - /*isExtern=*/false); - Globals.push_back(G); - - // Construct the string in storage. - const Pointer Ptr(G->block()); - for (unsigned I = 0, N = S->getLength(); I <= N; ++I) { - Pointer Field = Ptr.atIndex(I).narrow(); - const uint32_t CodePoint = I == N ? 0 : S->getCodeUnit(I); - switch (CharType) { - case PT_Sint8: { - using T = PrimConv<PT_Sint8>::T; - Field.deref<T>() = T::from(CodePoint, BitWidth); - break; - } - case PT_Uint16: { - using T = PrimConv<PT_Uint16>::T; - Field.deref<T>() = T::from(CodePoint, BitWidth); - break; - } - case PT_Uint32: { - using T = PrimConv<PT_Uint32>::T; - Field.deref<T>() = T::from(CodePoint, BitWidth); - break; - } - default: - llvm_unreachable("unsupported character type"); - } - } - return I; -} - -Pointer Program::getPtrGlobal(unsigned Idx) { - assert(Idx < Globals.size()); - return Pointer(Globals[Idx]->block()); -} - -llvm::Optional<unsigned> Program::getGlobal(const ValueDecl *VD) { - auto It = GlobalIndices.find(VD); - if (It != GlobalIndices.end()) - return It->second; - - // Find any previous declarations which were aleady evaluated. - llvm::Optional<unsigned> Index; - for (const Decl *P = VD; P; P = P->getPreviousDecl()) { - auto It = GlobalIndices.find(P); - if (It != GlobalIndices.end()) { - Index = It->second; - break; - } - } - - // Map the decl to the existing index. - if (Index) { - GlobalIndices[VD] = *Index; - return {}; - } - - return Index; -} - -llvm::Optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD) { - if (auto Idx = getGlobal(VD)) - return Idx; - - if (auto Idx = createGlobal(VD)) { - GlobalIndices[VD] = *Idx; - return Idx; - } - return {}; -} - -llvm::Optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) { - auto &ASTCtx = Ctx.getASTContext(); - - // Create a pointer to an incomplete array of the specified elements. - QualType ElemTy = PD->getType()->getAs<PointerType>()->getPointeeType(); - QualType Ty = ASTCtx.getIncompleteArrayType(ElemTy, ArrayType::Normal, 0); - - // Dedup blocks since they are immutable and pointers cannot be compared. - auto It = DummyParams.find(PD); - if (It != DummyParams.end()) - return It->second; - - if (auto Idx = createGlobal(PD, Ty, /*isStatic=*/true, /*isExtern=*/true)) { - DummyParams[PD] = *Idx; - return Idx; - } - return {}; -} - -llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) { - bool IsStatic, IsExtern; - if (auto *Var = dyn_cast<VarDecl>(VD)) { - IsStatic = !Var->hasLocalStorage(); - IsExtern = !Var->getAnyInitializer(); - } else { - IsStatic = false; - IsExtern = true; - } - if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern)) { - for (const Decl *P = VD; P; P = P->getPreviousDecl()) - GlobalIndices[P] = *Idx; - return *Idx; - } - return {}; -} - -llvm::Optional<unsigned> Program::createGlobal(const Expr *E) { - return createGlobal(E, E->getType(), /*isStatic=*/true, /*isExtern=*/false); -} - -llvm::Optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, - bool IsStatic, bool IsExtern) { - // Create a descriptor for the global. - Descriptor *Desc; - const bool IsConst = Ty.isConstQualified(); - const bool IsTemporary = D.dyn_cast<const Expr *>(); - if (auto T = Ctx.classify(Ty)) { - Desc = createDescriptor(D, *T, IsConst, IsTemporary); - } else { - Desc = createDescriptor(D, Ty.getTypePtr(), IsConst, IsTemporary); - } - if (!Desc) - return {}; - - // Allocate a block for storage. - unsigned I = Globals.size(); - - auto *G = new (Allocator, Desc->getAllocSize()) - Global(getCurrentDecl(), Desc, IsStatic, IsExtern); - G->block()->invokeCtor(); - - Globals.push_back(G); - - return I; -} - -Function *Program::getFunction(const FunctionDecl *F) { - F = F->getDefinition(); - auto It = Funcs.find(F); - return It == Funcs.end() ? nullptr : It->second.get(); -} - -llvm::Expected<Function *> Program::getOrCreateFunction(const FunctionDecl *F) { - if (Function *Func = getFunction(F)) { - return Func; - } - - // Try to compile the function if it wasn't compiled yet. - if (const FunctionDecl *FD = F->getDefinition()) - return ByteCodeStmtGen<ByteCodeEmitter>(Ctx, *this).compileFunc(FD); - - // A relocation which traps if not resolved. - return nullptr; -} - -Record *Program::getOrCreateRecord(const RecordDecl *RD) { - // Use the actual definition as a key. - RD = RD->getDefinition(); - if (!RD) - return nullptr; - - // Deduplicate records. - auto It = Records.find(RD); - if (It != Records.end()) { - return It->second; - } - - // Number of bytes required by fields and base classes. - unsigned Size = 0; - // Number of bytes required by virtual base. - unsigned VirtSize = 0; - - // Helper to get a base descriptor. - auto GetBaseDesc = [this](const RecordDecl *BD, Record *BR) -> Descriptor * { - if (!BR) - return nullptr; - return allocateDescriptor(BD, BR, /*isConst=*/false, - /*isTemporary=*/false, - /*isMutable=*/false); - }; - - // Reserve space for base classes. - Record::BaseList Bases; - Record::VirtualBaseList VirtBases; - if (auto *CD = dyn_cast<CXXRecordDecl>(RD)) { - for (const CXXBaseSpecifier &Spec : CD->bases()) { - if (Spec.isVirtual()) - continue; - - const RecordDecl *BD = Spec.getType()->getAs<RecordType>()->getDecl(); - Record *BR = getOrCreateRecord(BD); - if (Descriptor *Desc = GetBaseDesc(BD, BR)) { - Size += align(sizeof(InlineDescriptor)); - Bases.push_back({BD, Size, Desc, BR}); - Size += align(BR->getSize()); - continue; - } - return nullptr; - } - - for (const CXXBaseSpecifier &Spec : CD->vbases()) { - const RecordDecl *BD = Spec.getType()->getAs<RecordType>()->getDecl(); - Record *BR = getOrCreateRecord(BD); - - if (Descriptor *Desc = GetBaseDesc(BD, BR)) { - VirtSize += align(sizeof(InlineDescriptor)); - VirtBases.push_back({BD, VirtSize, Desc, BR}); - VirtSize += align(BR->getSize()); - continue; - } - return nullptr; - } - } - - // Reserve space for fields. - Record::FieldList Fields; - for (const FieldDecl *FD : RD->fields()) { - // Reserve space for the field's descriptor and the offset. - Size += align(sizeof(InlineDescriptor)); - - // Classify the field and add its metadata. - QualType FT = FD->getType(); - const bool IsConst = FT.isConstQualified(); - const bool IsMutable = FD->isMutable(); - Descriptor *Desc; - if (llvm::Optional<PrimType> T = Ctx.classify(FT)) { - Desc = createDescriptor(FD, *T, IsConst, /*isTemporary=*/false, - IsMutable); - } else { - Desc = createDescriptor(FD, FT.getTypePtr(), IsConst, - /*isTemporary=*/false, IsMutable); - } - if (!Desc) - return nullptr; - Fields.push_back({FD, Size, Desc}); - Size += align(Desc->getAllocSize()); - } - - Record *R = new (Allocator) Record(RD, std::move(Bases), std::move(Fields), - std::move(VirtBases), VirtSize, Size); - Records.insert({RD, R}); - return R; -} - -Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, - bool IsConst, bool IsTemporary, - bool IsMutable) { - // Classes and structures. - if (auto *RT = Ty->getAs<RecordType>()) { - if (auto *Record = getOrCreateRecord(RT->getDecl())) - return allocateDescriptor(D, Record, IsConst, IsTemporary, IsMutable); - } - - // Arrays. - if (auto ArrayType = Ty->getAsArrayTypeUnsafe()) { - QualType ElemTy = ArrayType->getElementType(); - // Array of well-known bounds. - if (auto CAT = dyn_cast<ConstantArrayType>(ArrayType)) { - size_t NumElems = CAT->getSize().getZExtValue(); - if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) { - // Arrays of primitives. - unsigned ElemSize = primSize(*T); - if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) { - return {}; - } - return allocateDescriptor(D, *T, NumElems, IsConst, IsTemporary, - IsMutable); - } else { - // Arrays of composites. In this case, the array is a list of pointers, - // followed by the actual elements. - Descriptor *Desc = - createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); - if (!Desc) - return nullptr; - InterpSize ElemSize = Desc->getAllocSize() + sizeof(InlineDescriptor); - if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) - return {}; - return allocateDescriptor(D, Desc, NumElems, IsConst, IsTemporary, - IsMutable); - } - } - - // Array of unknown bounds - cannot be accessed and pointer arithmetic - // is forbidden on pointers to such objects. - if (isa<IncompleteArrayType>(ArrayType)) { - if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) { - return allocateDescriptor(D, *T, IsTemporary, - Descriptor::UnknownSize{}); - } else { - Descriptor *Desc = - createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); - if (!Desc) - return nullptr; - return allocateDescriptor(D, Desc, IsTemporary, - Descriptor::UnknownSize{}); - } - } - } - - // Atomic types. - if (auto *AT = Ty->getAs<AtomicType>()) { - const Type *InnerTy = AT->getValueType().getTypePtr(); - return createDescriptor(D, InnerTy, IsConst, IsTemporary, IsMutable); - } - - // Complex types - represented as arrays of elements. - if (auto *CT = Ty->getAs<ComplexType>()) { - PrimType ElemTy = *Ctx.classify(CT->getElementType()); - return allocateDescriptor(D, ElemTy, 2, IsConst, IsTemporary, IsMutable); - } - - return nullptr; -} diff --git a/clang/lib/AST/Interp/Program.h b/clang/lib/AST/Interp/Program.h deleted file mode 100644 index 88b94432e89..00000000000 --- a/clang/lib/AST/Interp/Program.h +++ /dev/null @@ -1,220 +0,0 @@ -//===--- Program.h - Bytecode for the constexpr VM --------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines a program which organises and links multiple bytecode functions. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_PROGRAM_H -#define LLVM_CLANG_AST_INTERP_PROGRAM_H - -#include "Function.h" -#include "Pointer.h" -#include "Record.h" -#include "Source.h" -#include "Type.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/PointerUnion.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Allocator.h" -#include <map> -#include <vector> - -namespace clang { -class RecordDecl; -class Expr; -class FunctionDecl; -class Stmt; -class StringLiteral; -class VarDecl; - -namespace interp { -class Context; -class State; -class Record; -class Scope; - -/// The program contains and links the bytecode for all functions. -class Program { -public: - Program(Context &Ctx) : Ctx(Ctx) {} - - /// Emits a string literal among global data. - unsigned createGlobalString(const StringLiteral *S); - - /// Returns a pointer to a global. - Pointer getPtrGlobal(unsigned Idx); - - /// Returns the value of a global. - Block *getGlobal(unsigned Idx) { - assert(Idx < Globals.size()); - return Globals[Idx]->block(); - } - - /// Finds a global's index. - llvm::Optional<unsigned> getGlobal(const ValueDecl *VD); - - /// Returns or creates a global an creates an index to it. - llvm::Optional<unsigned> getOrCreateGlobal(const ValueDecl *VD); - - /// Returns or creates a dummy value for parameters. - llvm::Optional<unsigned> getOrCreateDummy(const ParmVarDecl *PD); - - /// Creates a global and returns its index. - llvm::Optional<unsigned> createGlobal(const ValueDecl *VD); - - /// Creates a global from a lifetime-extended temporary. - llvm::Optional<unsigned> createGlobal(const Expr *E); - - /// Creates a new function from a code range. - template <typename... Ts> - Function *createFunction(const FunctionDecl *Def, Ts &&... Args) { - auto *Func = new Function(*this, Def, std::forward<Ts>(Args)...); - Funcs.insert({Def, std::unique_ptr<Function>(Func)}); - return Func; - } - /// Creates an anonymous function. - template <typename... Ts> - Function *createFunction(Ts &&... Args) { - auto *Func = new Function(*this, std::forward<Ts>(Args)...); - AnonFuncs.emplace_back(Func); - return Func; - } - - /// Returns a function. - Function *getFunction(const FunctionDecl *F); - - /// Returns a pointer to a function if it exists and can be compiled. - /// If a function couldn't be compiled, an error is returned. - /// If a function was not yet defined, a null pointer is returned. - llvm::Expected<Function *> getOrCreateFunction(const FunctionDecl *F); - - /// Returns a record or creates one if it does not exist. - Record *getOrCreateRecord(const RecordDecl *RD); - - /// Creates a descriptor for a primitive type. - Descriptor *createDescriptor(const DeclTy &D, PrimType Type, - bool IsConst = false, - bool IsTemporary = false, - bool IsMutable = false) { - return allocateDescriptor(D, Type, IsConst, IsTemporary, IsMutable); - } - - /// Creates a descriptor for a composite type. - Descriptor *createDescriptor(const DeclTy &D, const Type *Ty, - bool IsConst = false, bool IsTemporary = false, - bool IsMutable = false); - - /// Context to manage declaration lifetimes. - class DeclScope { - public: - DeclScope(Program &P, const VarDecl *VD) : P(P) { P.startDeclaration(VD); } - ~DeclScope() { P.endDeclaration(); } - - private: - Program &P; - }; - - /// Returns the current declaration ID. - llvm::Optional<unsigned> getCurrentDecl() const { - if (CurrentDeclaration == NoDeclaration) - return llvm::Optional<unsigned>{}; - return LastDeclaration; - } - -private: - friend class DeclScope; - - llvm::Optional<unsigned> createGlobal(const DeclTy &D, QualType Ty, - bool IsStatic, bool IsExtern); - - /// Reference to the VM context. - Context &Ctx; - /// Mapping from decls to cached bytecode functions. - llvm::DenseMap<const FunctionDecl *, std::unique_ptr<Function>> Funcs; - /// List of anonymous functions. - std::vector<std::unique_ptr<Function>> AnonFuncs; - - /// Function relocation locations. - llvm::DenseMap<const FunctionDecl *, std::vector<unsigned>> Relocs; - - /// Custom allocator for global storage. - using PoolAllocTy = llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator>; - - /// Descriptor + storage for a global object. - /// - /// Global objects never go out of scope, thus they do not track pointers. - class Global { - public: - /// Create a global descriptor for string literals. - template <typename... Tys> - Global(Tys... Args) : B(std::forward<Tys>(Args)...) {} - - /// Allocates the global in the pool, reserving storate for data. - void *operator new(size_t Meta, PoolAllocTy &Alloc, size_t Data) { - return Alloc.Allocate(Meta + Data, alignof(void *)); - } - - /// Return a pointer to the data. - char *data() { return B.data(); } - /// Return a pointer to the block. - Block *block() { return &B; } - - private: - /// Required metadata - does not actually track pointers. - Block B; - }; - - /// Allocator for globals. - PoolAllocTy Allocator; - - /// Global objects. - std::vector<Global *> Globals; - /// Cached global indices. - llvm::DenseMap<const void *, unsigned> GlobalIndices; - - /// Mapping from decls to record metadata. - llvm::DenseMap<const RecordDecl *, Record *> Records; - - /// Dummy parameter to generate pointers from. - llvm::DenseMap<const ParmVarDecl *, unsigned> DummyParams; - - /// Creates a new descriptor. - template <typename... Ts> - Descriptor *allocateDescriptor(Ts &&... Args) { - return new (Allocator) Descriptor(std::forward<Ts>(Args)...); - } - - /// No declaration ID. - static constexpr unsigned NoDeclaration = (unsigned)-1; - /// Last declaration ID. - unsigned LastDeclaration = 0; - /// Current declaration ID. - unsigned CurrentDeclaration = NoDeclaration; - - /// Starts evaluating a declaration. - void startDeclaration(const VarDecl *Decl) { - LastDeclaration += 1; - CurrentDeclaration = LastDeclaration; - } - - /// Ends a global declaration. - void endDeclaration() { - CurrentDeclaration = NoDeclaration; - } - -public: - /// Dumps the disassembled bytecode to \c llvm::errs(). - void dump() const; - void dump(llvm::raw_ostream &OS) const; -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/Record.cpp b/clang/lib/AST/Interp/Record.cpp deleted file mode 100644 index f440c470505..00000000000 --- a/clang/lib/AST/Interp/Record.cpp +++ /dev/null @@ -1,46 +0,0 @@ -//===--- Record.cpp - struct and class metadata for the VM ------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Record.h" - -using namespace clang; -using namespace clang::interp; - -Record::Record(const RecordDecl *Decl, BaseList &&SrcBases, - FieldList &&SrcFields, VirtualBaseList &&SrcVirtualBases, - unsigned VirtualSize, unsigned BaseSize) - : Decl(Decl), Bases(std::move(SrcBases)), Fields(std::move(SrcFields)), - BaseSize(BaseSize), VirtualSize(VirtualSize) { - for (Base &V : SrcVirtualBases) - VirtualBases.push_back({ V.Decl, V.Offset + BaseSize, V.Desc, V.R }); - - for (Base &B : Bases) - BaseMap[B.Decl] = &B; - for (Field &F : Fields) - FieldMap[F.Decl] = &F; - for (Base &V : VirtualBases) - VirtualBaseMap[V.Decl] = &V; -} - -const Record::Field *Record::getField(const FieldDecl *FD) const { - auto It = FieldMap.find(FD); - assert(It != FieldMap.end() && "Missing field"); - return It->second; -} - -const Record::Base *Record::getBase(const RecordDecl *FD) const { - auto It = BaseMap.find(FD); - assert(It != BaseMap.end() && "Missing base"); - return It->second; -} - -const Record::Base *Record::getVirtualBase(const RecordDecl *FD) const { - auto It = VirtualBaseMap.find(FD); - assert(It != VirtualBaseMap.end() && "Missing virtual base"); - return It->second; -} diff --git a/clang/lib/AST/Interp/Record.h b/clang/lib/AST/Interp/Record.h deleted file mode 100644 index 9cdee900375..00000000000 --- a/clang/lib/AST/Interp/Record.h +++ /dev/null @@ -1,121 +0,0 @@ -//===--- Record.h - struct and class metadata for the VM --------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// A record is part of a program to describe the layout and methods of a struct. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_RECORD_H -#define LLVM_CLANG_AST_INTERP_RECORD_H - -#include "Pointer.h" - -namespace clang { -namespace interp { -class Program; - -/// Structure/Class descriptor. -class Record { -public: - /// Describes a record field. - struct Field { - const FieldDecl *Decl; - unsigned Offset; - Descriptor *Desc; - }; - - /// Describes a base class. - struct Base { - const RecordDecl *Decl; - unsigned Offset; - Descriptor *Desc; - Record *R; - }; - - /// Mapping from identifiers to field descriptors. - using FieldList = llvm::SmallVector<Field, 8>; - /// Mapping from identifiers to base classes. - using BaseList = llvm::SmallVector<Base, 8>; - /// List of virtual base classes. - using VirtualBaseList = llvm::SmallVector<Base, 2>; - -public: - /// Returns the underlying declaration. - const RecordDecl *getDecl() const { return Decl; } - /// Checks if the record is a union. - bool isUnion() const { return getDecl()->isUnion(); } - /// Returns the size of the record. - unsigned getSize() const { return BaseSize; } - /// Returns the full size of the record, including records. - unsigned getFullSize() const { return BaseSize + VirtualSize; } - /// Returns a field. - const Field *getField(const FieldDecl *FD) const; - /// Returns a base descriptor. - const Base *getBase(const RecordDecl *FD) const; - /// Returns a virtual base descriptor. - const Base *getVirtualBase(const RecordDecl *RD) const; - - using const_field_iter = FieldList::const_iterator; - llvm::iterator_range<const_field_iter> fields() const { - return llvm::make_range(Fields.begin(), Fields.end()); - } - - unsigned getNumFields() { return Fields.size(); } - Field *getField(unsigned I) { return &Fields[I]; } - - using const_base_iter = BaseList::const_iterator; - llvm::iterator_range<const_base_iter> bases() const { - return llvm::make_range(Bases.begin(), Bases.end()); - } - - unsigned getNumBases() { return Bases.size(); } - Base *getBase(unsigned I) { return &Bases[I]; } - - using const_virtual_iter = VirtualBaseList::const_iterator; - llvm::iterator_range<const_virtual_iter> virtual_bases() const { - return llvm::make_range(VirtualBases.begin(), VirtualBases.end()); - } - - unsigned getNumVirtualBases() { return VirtualBases.size(); } - Base *getVirtualBase(unsigned I) { return &VirtualBases[I]; } - -private: - /// Constructor used by Program to create record descriptors. - Record(const RecordDecl *, BaseList &&Bases, FieldList &&Fields, - VirtualBaseList &&VirtualBases, unsigned VirtualSize, - unsigned BaseSize); - -private: - friend class Program; - - /// Original declaration. - const RecordDecl *Decl; - /// List of all base classes. - BaseList Bases; - /// List of all the fields in the record. - FieldList Fields; - /// List o fall virtual bases. - VirtualBaseList VirtualBases; - - /// Mapping from declarations to bases. - llvm::DenseMap<const RecordDecl *, Base *> BaseMap; - /// Mapping from field identifiers to descriptors. - llvm::DenseMap<const FieldDecl *, Field *> FieldMap; - /// Mapping from declarations to virtual bases. - llvm::DenseMap<const RecordDecl *, Base *> VirtualBaseMap; - /// Mapping from - /// Size of the structure. - unsigned BaseSize; - /// Size of all virtual bases. - unsigned VirtualSize; -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/Source.cpp b/clang/lib/AST/Interp/Source.cpp deleted file mode 100644 index 4bec8781263..00000000000 --- a/clang/lib/AST/Interp/Source.cpp +++ /dev/null @@ -1,39 +0,0 @@ -//===--- Source.cpp - Source expression tracking ----------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Source.h" -#include "clang/AST/Expr.h" - -using namespace clang; -using namespace clang::interp; - -SourceLocation SourceInfo::getLoc() const { - if (const Expr *E = asExpr()) - return E->getExprLoc(); - if (const Stmt *S = asStmt()) - return S->getBeginLoc(); - if (const Decl *D = asDecl()) - return D->getBeginLoc(); - return SourceLocation(); -} - -const Expr *SourceInfo::asExpr() const { - if (auto *S = Source.dyn_cast<const Stmt *>()) - return dyn_cast<Expr>(S); - return nullptr; -} - -const Expr *SourceMapper::getExpr(Function *F, CodePtr PC) const { - if (const Expr *E = getSource(F, PC).asExpr()) - return E; - llvm::report_fatal_error("missing source expression"); -} - -SourceLocation SourceMapper::getLocation(Function *F, CodePtr PC) const { - return getSource(F, PC).getLoc(); -} diff --git a/clang/lib/AST/Interp/Source.h b/clang/lib/AST/Interp/Source.h deleted file mode 100644 index e591c3399d7..00000000000 --- a/clang/lib/AST/Interp/Source.h +++ /dev/null @@ -1,118 +0,0 @@ -//===--- Source.h - Source location provider for the VM --------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines a program which organises and links multiple bytecode functions. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_SOURCE_H -#define LLVM_CLANG_AST_INTERP_SOURCE_H - -#include "clang/AST/Decl.h" -#include "clang/AST/Stmt.h" -#include "llvm/Support/Endian.h" - -namespace clang { -namespace interp { -class Function; - -/// Pointer into the code segment. -class CodePtr { -public: - CodePtr() : Ptr(nullptr) {} - - CodePtr &operator+=(int32_t Offset) { - Ptr += Offset; - return *this; - } - - int32_t operator-(const CodePtr &RHS) const { - assert(Ptr != nullptr && RHS.Ptr != nullptr && "Invalid code pointer"); - return Ptr - RHS.Ptr; - } - - CodePtr operator-(size_t RHS) const { - assert(Ptr != nullptr && "Invalid code pointer"); - return CodePtr(Ptr - RHS); - } - - bool operator!=(const CodePtr &RHS) const { return Ptr != RHS.Ptr; } - - /// Reads data and advances the pointer. - template <typename T> T read() { - T Value = ReadHelper<T>(Ptr); - Ptr += sizeof(T); - return Value; - } - -private: - /// Constructor used by Function to generate pointers. - CodePtr(const char *Ptr) : Ptr(Ptr) {} - - /// Helper to decode a value or a pointer. - template <typename T> - static typename std::enable_if<!std::is_pointer<T>::value, T>::type - ReadHelper(const char *Ptr) { - using namespace llvm::support; - return endian::read<T, endianness::native, 1>(Ptr); - } - - template <typename T> - static typename std::enable_if<std::is_pointer<T>::value, T>::type - ReadHelper(const char *Ptr) { - using namespace llvm::support; - auto Punned = endian::read<uintptr_t, endianness::native, 1>(Ptr); - return reinterpret_cast<T>(Punned); - } - -private: - friend class Function; - - /// Pointer into the code owned by a function. - const char *Ptr; -}; - -/// Describes the statement/declaration an opcode was generated from. -class SourceInfo { -public: - SourceInfo() {} - SourceInfo(const Stmt *E) : Source(E) {} - SourceInfo(const Decl *D) : Source(D) {} - - SourceLocation getLoc() const; - - const Stmt *asStmt() const { return Source.dyn_cast<const Stmt *>(); } - const Decl *asDecl() const { return Source.dyn_cast<const Decl *>(); } - const Expr *asExpr() const; - - operator bool() const { return !Source.isNull(); } - -private: - llvm::PointerUnion<const Decl *, const Stmt *> Source; -}; - -using SourceMap = std::vector<std::pair<unsigned, SourceInfo>>; - -/// Interface for classes which map locations to sources. -class SourceMapper { -public: - virtual ~SourceMapper() {} - - /// Returns source information for a given PC in a function. - virtual SourceInfo getSource(Function *F, CodePtr PC) const = 0; - - /// Returns the expression if an opcode belongs to one, null otherwise. - const Expr *getExpr(Function *F, CodePtr PC) const; - /// Returns the location from which an opcode originates. - SourceLocation getLocation(Function *F, CodePtr PC) const; -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/State.cpp b/clang/lib/AST/Interp/State.cpp deleted file mode 100644 index 692cc2e8d69..00000000000 --- a/clang/lib/AST/Interp/State.cpp +++ /dev/null @@ -1,158 +0,0 @@ -//===--- State.cpp - State chain for the VM and AST Walker ------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "State.h" -#include "Frame.h" -#include "Program.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/CXXInheritance.h" - -using namespace clang; -using namespace clang::interp; - -State::~State() {} - -OptionalDiagnostic State::FFDiag(SourceLocation Loc, diag::kind DiagId, - unsigned ExtraNotes) { - return diag(Loc, DiagId, ExtraNotes, false); -} - -OptionalDiagnostic State::FFDiag(const Expr *E, diag::kind DiagId, - unsigned ExtraNotes) { - if (getEvalStatus().Diag) - return diag(E->getExprLoc(), DiagId, ExtraNotes, false); - setActiveDiagnostic(false); - return OptionalDiagnostic(); -} - -OptionalDiagnostic State::FFDiag(const SourceInfo &SI, diag::kind DiagId, - unsigned ExtraNotes) { - if (getEvalStatus().Diag) - return diag(SI.getLoc(), DiagId, ExtraNotes, false); - setActiveDiagnostic(false); - return OptionalDiagnostic(); -} - -OptionalDiagnostic State::CCEDiag(SourceLocation Loc, diag::kind DiagId, - unsigned ExtraNotes) { - // Don't override a previous diagnostic. Don't bother collecting - // diagnostics if we're evaluating for overflow. - if (!getEvalStatus().Diag || !getEvalStatus().Diag->empty()) { - setActiveDiagnostic(false); - return OptionalDiagnostic(); - } - return diag(Loc, DiagId, ExtraNotes, true); -} - -OptionalDiagnostic State::CCEDiag(const Expr *E, diag::kind DiagId, - unsigned ExtraNotes) { - return CCEDiag(E->getExprLoc(), DiagId, ExtraNotes); -} - -OptionalDiagnostic State::CCEDiag(const SourceInfo &SI, diag::kind DiagId, - unsigned ExtraNotes) { - return CCEDiag(SI.getLoc(), DiagId, ExtraNotes); -} - -OptionalDiagnostic State::Note(SourceLocation Loc, diag::kind DiagId) { - if (!hasActiveDiagnostic()) - return OptionalDiagnostic(); - return OptionalDiagnostic(&addDiag(Loc, DiagId)); -} - -void State::addNotes(ArrayRef<PartialDiagnosticAt> Diags) { - if (hasActiveDiagnostic()) { - getEvalStatus().Diag->insert(getEvalStatus().Diag->end(), Diags.begin(), - Diags.end()); - } -} - -DiagnosticBuilder State::report(SourceLocation Loc, diag::kind DiagId) { - return getCtx().getDiagnostics().Report(Loc, DiagId); -} - -/// Add a diagnostic to the diagnostics list. -PartialDiagnostic &State::addDiag(SourceLocation Loc, diag::kind DiagId) { - PartialDiagnostic PD(DiagId, getCtx().getDiagAllocator()); - getEvalStatus().Diag->push_back(std::make_pair(Loc, PD)); - return getEvalStatus().Diag->back().second; -} - -OptionalDiagnostic State::diag(SourceLocation Loc, diag::kind DiagId, - unsigned ExtraNotes, bool IsCCEDiag) { - Expr::EvalStatus &EvalStatus = getEvalStatus(); - if (EvalStatus.Diag) { - if (hasPriorDiagnostic()) { - return OptionalDiagnostic(); - } - - unsigned CallStackNotes = getCallStackDepth() - 1; - unsigned Limit = getCtx().getDiagnostics().getConstexprBacktraceLimit(); - if (Limit) - CallStackNotes = std::min(CallStackNotes, Limit + 1); - if (checkingPotentialConstantExpression()) - CallStackNotes = 0; - - setActiveDiagnostic(true); - setFoldFailureDiagnostic(!IsCCEDiag); - EvalStatus.Diag->clear(); - EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes); - addDiag(Loc, DiagId); - if (!checkingPotentialConstantExpression()) { - addCallStack(Limit); - } - return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second); - } - setActiveDiagnostic(false); - return OptionalDiagnostic(); -} - -const LangOptions &State::getLangOpts() const { return getCtx().getLangOpts(); } - -void State::addCallStack(unsigned Limit) { - // Determine which calls to skip, if any. - unsigned ActiveCalls = getCallStackDepth() - 1; - unsigned SkipStart = ActiveCalls, SkipEnd = SkipStart; - if (Limit && Limit < ActiveCalls) { - SkipStart = Limit / 2 + Limit % 2; - SkipEnd = ActiveCalls - Limit / 2; - } - - // Walk the call stack and add the diagnostics. - unsigned CallIdx = 0; - Frame *Top = getCurrentFrame(); - const Frame *Bottom = getBottomFrame(); - for (Frame *F = Top; F != Bottom; F = F->getCaller(), ++CallIdx) { - SourceLocation CallLocation = F->getCallLocation(); - - // Skip this call? - if (CallIdx >= SkipStart && CallIdx < SkipEnd) { - if (CallIdx == SkipStart) { - // Note that we're skipping calls. - addDiag(CallLocation, diag::note_constexpr_calls_suppressed) - << unsigned(ActiveCalls - Limit); - } - continue; - } - - // Use a different note for an inheriting constructor, because from the - // user's perspective it's not really a function at all. - if (auto *CD = dyn_cast_or_null<CXXConstructorDecl>(F->getCallee())) { - if (CD->isInheritingConstructor()) { - addDiag(CallLocation, diag::note_constexpr_inherited_ctor_call_here) - << CD->getParent(); - continue; - } - } - - SmallVector<char, 128> Buffer; - llvm::raw_svector_ostream Out(Buffer); - F->describe(Out); - addDiag(CallLocation, diag::note_constexpr_call_here) << Out.str(); - } -} diff --git a/clang/lib/AST/Interp/State.h b/clang/lib/AST/Interp/State.h deleted file mode 100644 index 9965aa3182b..00000000000 --- a/clang/lib/AST/Interp/State.h +++ /dev/null @@ -1,130 +0,0 @@ -//===--- State.h - State chain for the VM and AST Walker --------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the base class of the interpreter and evaluator state. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_STATE_H -#define LLVM_CLANG_AST_INTERP_STATE_H - -#include "clang/AST/ASTDiagnostic.h" -#include "clang/AST/Expr.h" -#include "clang/Basic/OptionalDiagnostic.h" - -namespace clang { - -/// Kinds of access we can perform on an object, for diagnostics. Note that -/// we consider a member function call to be a kind of access, even though -/// it is not formally an access of the object, because it has (largely) the -/// same set of semantic restrictions. -enum AccessKinds { - AK_Read, - AK_Assign, - AK_Increment, - AK_Decrement, - AK_MemberCall, - AK_DynamicCast, - AK_TypeId, -}; - -// The order of this enum is important for diagnostics. -enum CheckSubobjectKind { - CSK_Base, - CSK_Derived, - CSK_Field, - CSK_ArrayToPointer, - CSK_ArrayIndex, - CSK_Real, - CSK_Imag -}; - -namespace interp { -class Frame; -class SourceInfo; - -/// Interface for the VM to interact with the AST walker's context. -class State { -public: - virtual ~State(); - - virtual bool checkingForOverflow() const = 0; - virtual bool checkingPotentialConstantExpression() const = 0; - virtual bool noteUndefinedBehavior() = 0; - virtual bool keepEvaluatingAfterFailure() const = 0; - virtual Frame *getCurrentFrame() = 0; - virtual const Frame *getBottomFrame() const = 0; - virtual bool hasActiveDiagnostic() = 0; - virtual void setActiveDiagnostic(bool Flag) = 0; - virtual void setFoldFailureDiagnostic(bool Flag) = 0; - virtual Expr::EvalStatus &getEvalStatus() const = 0; - virtual ASTContext &getCtx() const = 0; - virtual bool hasPriorDiagnostic() = 0; - virtual unsigned getCallStackDepth() = 0; - -public: - // Diagnose that the evaluation could not be folded (FF => FoldFailure) - OptionalDiagnostic - FFDiag(SourceLocation Loc, - diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, - unsigned ExtraNotes = 0); - - OptionalDiagnostic - FFDiag(const Expr *E, - diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, - unsigned ExtraNotes = 0); - - OptionalDiagnostic - FFDiag(const SourceInfo &SI, - diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, - unsigned ExtraNotes = 0); - - /// Diagnose that the evaluation does not produce a C++11 core constant - /// expression. - /// - /// FIXME: Stop evaluating if we're in EM_ConstantExpression or - /// EM_PotentialConstantExpression mode and we produce one of these. - OptionalDiagnostic - CCEDiag(SourceLocation Loc, - diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, - unsigned ExtraNotes = 0); - - OptionalDiagnostic - CCEDiag(const Expr *E, - diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, - unsigned ExtraNotes = 0); - - OptionalDiagnostic - CCEDiag(const SourceInfo &SI, - diag::kind DiagId = diag::note_invalid_subexpr_in_const_expr, - unsigned ExtraNotes = 0); - - /// Add a note to a prior diagnostic. - OptionalDiagnostic Note(SourceLocation Loc, diag::kind DiagId); - - /// Add a stack of notes to a prior diagnostic. - void addNotes(ArrayRef<PartialDiagnosticAt> Diags); - - /// Directly reports a diagnostic message. - DiagnosticBuilder report(SourceLocation Loc, diag::kind DiagId); - - const LangOptions &getLangOpts() const; - -private: - void addCallStack(unsigned Limit); - - PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId); - - OptionalDiagnostic diag(SourceLocation Loc, diag::kind DiagId, - unsigned ExtraNotes, bool IsCCEDiag); -}; - -} // namespace interp -} // namespace clang - -#endif diff --git a/clang/lib/AST/Interp/Type.cpp b/clang/lib/AST/Interp/Type.cpp deleted file mode 100644 index 0d7ca404e23..00000000000 --- a/clang/lib/AST/Interp/Type.cpp +++ /dev/null @@ -1,23 +0,0 @@ -//===--- Type.cpp - Types for the constexpr VM ------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "Type.h" - -using namespace clang; -using namespace clang::interp; - -namespace clang { -namespace interp { - -size_t primSize(PrimType Type) { - TYPE_SWITCH(Type, return sizeof(T)); - llvm_unreachable("not a primitive type"); -} - -} // namespace interp -} // namespace clang diff --git a/clang/lib/AST/Interp/Type.h b/clang/lib/AST/Interp/Type.h deleted file mode 100644 index 4035fdb3d72..00000000000 --- a/clang/lib/AST/Interp/Type.h +++ /dev/null @@ -1,114 +0,0 @@ -//===--- Type.h - Types for the constexpr VM --------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// Defines the VM types and helpers operating on types. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_AST_INTERP_TYPE_H -#define LLVM_CLANG_AST_INTERP_TYPE_H - -#include "Integral.h" -#include "Pointer.h" -#include <cstddef> -#include <cstdint> -#include <climits> - -namespace clang { -namespace interp { - -/// Enumeration of the primitive types of the VM. -enum PrimType : unsigned { - PT_Sint8, - PT_Uint8, - PT_Sint16, - PT_Uint16, - PT_Sint32, - PT_Uint32, - PT_Sint64, - PT_Uint64, - PT_Bool, - PT_Ptr, -}; - -/// Mapping from primitive types to their representation. -template <PrimType T> struct PrimConv; -template <> struct PrimConv<PT_Sint8> { using T = Integral<8, true>; }; -template <> struct PrimConv<PT_Uint8> { using T = Integral<8, false>; }; -template <> struct PrimConv<PT_Sint16> { using T = Integral<16, true>; }; -template <> struct PrimConv<PT_Uint16> { using T = Integral<16, false>; }; -template <> struct PrimConv<PT_Sint32> { using T = Integral<32, true>; }; -template <> struct PrimConv<PT_Uint32> { using T = Integral<32, false>; }; -template <> struct PrimConv<PT_Sint64> { using T = Integral<64, true>; }; -template <> struct PrimConv<PT_Uint64> { using T = Integral<64, false>; }; -template <> struct PrimConv<PT_Bool> { using T = Integral<1, false>; }; -template <> struct PrimConv<PT_Ptr> { using T = Pointer; }; - -/// Returns the size of a primitive type in bytes. -size_t primSize(PrimType Type); - -/// Aligns a size to the pointer alignment. -constexpr size_t align(size_t Size) { - return ((Size + alignof(void *) - 1) / alignof(void *)) * alignof(void *); -} - -inline bool isPrimitiveIntegral(PrimType Type) { - switch (Type) { - case PT_Bool: - case PT_Sint8: - case PT_Uint8: - case PT_Sint16: - case PT_Uint16: - case PT_Sint32: - case PT_Uint32: - case PT_Sint64: - case PT_Uint64: - return true; - default: - return false; - } -} - -} // namespace interp -} // namespace clang - -/// Helper macro to simplify type switches. -/// The macro implicitly exposes a type T in the scope of the inner block. -#define TYPE_SWITCH_CASE(Name, B) \ - case Name: { using T = PrimConv<Name>::T; do {B;} while(0); break; } -#define TYPE_SWITCH(Expr, B) \ - switch (Expr) { \ - TYPE_SWITCH_CASE(PT_Sint8, B) \ - TYPE_SWITCH_CASE(PT_Uint8, B) \ - TYPE_SWITCH_CASE(PT_Sint16, B) \ - TYPE_SWITCH_CASE(PT_Uint16, B) \ - TYPE_SWITCH_CASE(PT_Sint32, B) \ - TYPE_SWITCH_CASE(PT_Uint32, B) \ - TYPE_SWITCH_CASE(PT_Sint64, B) \ - TYPE_SWITCH_CASE(PT_Uint64, B) \ - TYPE_SWITCH_CASE(PT_Bool, B) \ - TYPE_SWITCH_CASE(PT_Ptr, B) \ - } -#define COMPOSITE_TYPE_SWITCH(Expr, B, D) \ - switch (Expr) { \ - TYPE_SWITCH_CASE(PT_Ptr, B) \ - default: do { D; } while(0); break; \ - } -#define INT_TYPE_SWITCH(Expr, B) \ - switch (Expr) { \ - TYPE_SWITCH_CASE(PT_Sint8, B) \ - TYPE_SWITCH_CASE(PT_Uint8, B) \ - TYPE_SWITCH_CASE(PT_Sint16, B) \ - TYPE_SWITCH_CASE(PT_Uint16, B) \ - TYPE_SWITCH_CASE(PT_Sint32, B) \ - TYPE_SWITCH_CASE(PT_Uint32, B) \ - TYPE_SWITCH_CASE(PT_Sint64, B) \ - TYPE_SWITCH_CASE(PT_Uint64, B) \ - default: llvm_unreachable("not an integer"); \ - } -#endif |