diff options
Diffstat (limited to 'clang/lib/AST/ByteCode')
-rw-r--r-- | clang/lib/AST/ByteCode/ByteCodeEmitter.cpp | 47 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/ByteCodeEmitter.h | 3 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/Compiler.cpp | 104 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/Compiler.h | 2 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/Context.cpp | 11 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/Context.h | 2 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/Descriptor.cpp | 46 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/EvalEmitter.cpp | 13 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/EvalEmitter.h | 1 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/Function.cpp | 1 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/Function.h | 6 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/Interp.cpp | 50 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/Interp.h | 15 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/InterpFrame.cpp | 17 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/InterpShared.cpp | 18 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/InterpState.cpp | 24 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/PrimType.h | 1 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/Program.cpp | 1 | ||||
-rw-r--r-- | clang/lib/AST/ByteCode/State.cpp | 1 |
19 files changed, 259 insertions, 104 deletions
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp index 3288585..1d71708 100644 --- a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp +++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp @@ -135,25 +135,25 @@ int32_t ByteCodeEmitter::getOffset(LabelTy Label) { /// Helper to write bytecode and bail out if 32-bit offsets become invalid. /// Pointers will be automatically marshalled as 32-bit IDs. template <typename T> -static void emit(Program &P, std::vector<std::byte> &Code, const T &Val, - bool &Success) { +static void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code, + const T &Val, bool &Success) { + size_t ValPos = Code.size(); size_t Size; if constexpr (std::is_pointer_v<T>) - Size = sizeof(uint32_t); + Size = align(sizeof(uint32_t)); else - Size = sizeof(T); + Size = align(sizeof(T)); - if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { + if (ValPos + Size > std::numeric_limits<unsigned>::max()) { Success = false; return; } // Access must be aligned! - size_t ValPos = align(Code.size()); - Size = align(Size); + assert(aligned(ValPos)); assert(aligned(ValPos + Size)); - Code.resize(ValPos + Size); + Code.resize_for_overwrite(ValPos + Size); if constexpr (!std::is_pointer_v<T>) { new (Code.data() + ValPos) T(Val); @@ -166,46 +166,45 @@ static void emit(Program &P, std::vector<std::byte> &Code, const T &Val, /// Emits a serializable value. These usually (potentially) contain /// heap-allocated memory and aren't trivially copyable. template <typename T> -static void emitSerialized(std::vector<std::byte> &Code, const T &Val, +static void emitSerialized(llvm::SmallVectorImpl<std::byte> &Code, const T &Val, bool &Success) { - size_t Size = Val.bytesToSerialize(); + size_t ValPos = Code.size(); + size_t Size = align(Val.bytesToSerialize()); - if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { + if (ValPos + Size > std::numeric_limits<unsigned>::max()) { Success = false; return; } // Access must be aligned! - assert(aligned(Code.size())); - size_t ValPos = Code.size(); - Size = align(Size); + assert(aligned(ValPos)); assert(aligned(ValPos + Size)); - Code.resize(ValPos + Size); + Code.resize_for_overwrite(ValPos + Size); Val.serialize(Code.data() + ValPos); } template <> -void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val, - bool &Success) { +void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code, + const Floating &Val, bool &Success) { emitSerialized(Code, Val, Success); } template <> -void emit(Program &P, std::vector<std::byte> &Code, +void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code, const IntegralAP<false> &Val, bool &Success) { emitSerialized(Code, Val, Success); } template <> -void emit(Program &P, std::vector<std::byte> &Code, const IntegralAP<true> &Val, - bool &Success) { +void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code, + const IntegralAP<true> &Val, bool &Success) { emitSerialized(Code, Val, Success); } template <> -void emit(Program &P, std::vector<std::byte> &Code, const FixedPoint &Val, - bool &Success) { +void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code, + const FixedPoint &Val, bool &Success) { emitSerialized(Code, Val, Success); } @@ -217,7 +216,9 @@ bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &...Args, // The opcode is followed by arguments. The source info is // attached to the address after the opcode. emit(P, Code, Op, Success); - if (SI) + if (LocOverride) + SrcMap.emplace_back(Code.size(), *LocOverride); + else if (SI) SrcMap.emplace_back(Code.size(), SI); (..., emit(P, Code, Args, Success)); diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.h b/clang/lib/AST/ByteCode/ByteCodeEmitter.h index 9e9dd5e..d29db66 100644 --- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h +++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h @@ -73,6 +73,7 @@ protected: ParamOffset LambdaThisCapture{0, false}; /// Local descriptors. llvm::SmallVector<SmallVector<Local, 8>, 2> Descriptors; + std::optional<SourceInfo> LocOverride = std::nullopt; private: /// Current compilation context. @@ -88,7 +89,7 @@ private: /// Location of label relocations. llvm::DenseMap<LabelTy, llvm::SmallVector<unsigned, 5>> LabelRelocs; /// Program code. - std::vector<std::byte> Code; + llvm::SmallVector<std::byte> Code; /// Opcode to expression mapping. SourceMap SrcMap; diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 8b9e5e0..cc99efa 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -185,6 +185,31 @@ private: bool OldFlag; }; +/// When generating code for e.g. implicit field initializers in constructors, +/// we don't have anything to point to in case the initializer causes an error. +/// In that case, we need to disable location tracking for the initializer so +/// we later point to the call range instead. +template <class Emitter> class LocOverrideScope final { +public: + LocOverrideScope(Compiler<Emitter> *Ctx, SourceInfo NewValue, + bool Enabled = true) + : Ctx(Ctx), OldFlag(Ctx->LocOverride), Enabled(Enabled) { + + if (Enabled) + Ctx->LocOverride = NewValue; + } + + ~LocOverrideScope() { + if (Enabled) + Ctx->LocOverride = OldFlag; + } + +private: + Compiler<Emitter> *Ctx; + std::optional<SourceInfo> OldFlag; + bool Enabled; +}; + } // namespace interp } // namespace clang @@ -201,6 +226,28 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) { return this->emitInvalidCast(CastKind::Volatile, /*Fatal=*/true, CE); OptPrimType SubExprT = classify(SubExpr->getType()); + // Try to load the value directly. This is purely a performance + // optimization. + if (SubExprT) { + if (const auto *DRE = dyn_cast<DeclRefExpr>(SubExpr)) { + const ValueDecl *D = DRE->getDecl(); + bool IsReference = D->getType()->isReferenceType(); + + if (!IsReference) { + if (Context::shouldBeGloballyIndexed(D)) { + if (auto GlobalIndex = P.getGlobal(D)) + return this->emitGetGlobal(*SubExprT, *GlobalIndex, CE); + } else if (auto It = Locals.find(D); It != Locals.end()) { + return this->emitGetLocal(*SubExprT, It->second.Offset, CE); + } else if (const auto *PVD = dyn_cast<ParmVarDecl>(D)) { + if (auto It = this->Params.find(PVD); It != this->Params.end()) { + return this->emitGetParam(*SubExprT, It->second.Offset, CE); + } + } + } + } + } + // Prepare storage for the result. if (!Initializing && !SubExprT) { std::optional<unsigned> LocalIndex = allocateLocal(SubExpr); @@ -331,6 +378,8 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) { } case CK_FloatingToIntegral: { + if (!CE->getType()->isIntegralOrEnumerationType()) + return false; if (!this->visit(SubExpr)) return false; PrimType ToT = classifyPrim(CE); @@ -1369,10 +1418,15 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) { // BitAdd/BitOr/BitXor/Shl/Shr doesn't support bool type, we need perform the // integer promotion. bool NeedIntPromot = ElemT == PT_Bool && (E->isBitwiseOp() || E->isShiftOp()); - QualType PromotTy = - Ctx.getASTContext().getPromotedIntegerType(Ctx.getASTContext().BoolTy); - PrimType PromotT = classifyPrim(PromotTy); - PrimType OpT = NeedIntPromot ? PromotT : ElemT; + QualType PromotTy; + PrimType PromotT = PT_Bool; + PrimType OpT = ElemT; + if (NeedIntPromot) { + PromotTy = + Ctx.getASTContext().getPromotedIntegerType(Ctx.getASTContext().BoolTy); + PromotT = classifyPrim(PromotTy); + OpT = PromotT; + } auto getElem = [=](unsigned Offset, PrimType ElemT, unsigned Index) { if (!this->emitGetLocal(PT_Ptr, Offset, E)) @@ -2009,7 +2063,12 @@ bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args, const FunctionDecl *FuncDecl, bool Activate) { assert(VarScope->getKind() == ScopeKind::Call); - llvm::BitVector NonNullArgs = collectNonNullArgs(FuncDecl, Args); + bool HasNonNullAttr = false; + llvm::BitVector NonNullArgs; + if (FuncDecl && FuncDecl->hasAttr<NonNullAttr>()) { + HasNonNullAttr = true; + NonNullArgs = collectNonNullArgs(FuncDecl, Args); + } unsigned ArgIndex = 0; for (const Expr *Arg : Args) { @@ -2035,7 +2094,7 @@ bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args, return false; } - if (FuncDecl && NonNullArgs[ArgIndex]) { + if (HasNonNullAttr && NonNullArgs[ArgIndex]) { PrimType ArgT = classify(Arg).value_or(PT_Ptr); if (ArgT == PT_Ptr) { if (!this->emitCheckNonNullArg(ArgT, Arg)) @@ -3850,10 +3909,7 @@ template <class Emitter> bool Compiler<Emitter>::VisitAddrLabelExpr(const AddrLabelExpr *E) { assert(E->getType()->isVoidPointerType()); - unsigned Offset = - allocateLocalPrimitive(E->getLabel(), PT_Ptr, /*IsConst=*/true); - - return this->emitGetLocal(PT_Ptr, Offset, E); + return this->emitDummyPtr(E, E); } template <class Emitter> @@ -5888,7 +5944,7 @@ bool Compiler<Emitter>::emitLambdaStaticInvokerBody(const CXXMethodDecl *MD) { const CXXRecordDecl *ClosureClass = MD->getParent(); const CXXMethodDecl *LambdaCallOp = ClosureClass->getLambdaCallOperator(); - assert(ClosureClass->captures_begin() == ClosureClass->captures_end()); + assert(ClosureClass->captures().empty()); const Function *Func = this->getFunction(LambdaCallOp); if (!Func) return false; @@ -5941,6 +5997,23 @@ bool Compiler<Emitter>::checkLiteralType(const Expr *E) { return this->emitCheckLiteralType(E->getType().getTypePtr(), E); } +static bool initNeedsOverridenLoc(const CXXCtorInitializer *Init) { + const Expr *InitExpr = Init->getInit(); + + if (!Init->isWritten() && !Init->isInClassMemberInitializer() && + !isa<CXXConstructExpr>(InitExpr)) + return true; + + if (const auto *CE = dyn_cast<CXXConstructExpr>(InitExpr)) { + const CXXConstructorDecl *Ctor = CE->getConstructor(); + if (Ctor->isDefaulted() && Ctor->isCopyOrMoveConstructor() && + Ctor->isTrivial()) + return true; + } + + return false; +} + template <class Emitter> bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) { assert(!ReturnType); @@ -5986,6 +6059,8 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) { bool IsUnion = R->isUnion(); if (IsUnion && Ctor->isCopyOrMoveConstructor()) { + LocOverrideScope<Emitter> LOS(this, SourceInfo{}); + if (R->getNumFields() == 0) return this->emitRetVoid(Ctor); // union copy and move ctors are special. @@ -6012,6 +6087,8 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) { if (const FieldDecl *Member = Init->getMember()) { const Record::Field *F = R->getField(Member); + LocOverrideScope<Emitter> LOS(this, SourceInfo{}, + initNeedsOverridenLoc(Init)); if (!emitFieldInitializer(F, F->Offset, InitExpr, IsUnion)) return false; } else if (const Type *Base = Init->getBaseClass()) { @@ -6040,7 +6117,8 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) { if (!this->emitFinishInitPop(InitExpr)) return false; } else if (const IndirectFieldDecl *IFD = Init->getIndirectMember()) { - + LocOverrideScope<Emitter> LOS(this, SourceInfo{}, + initNeedsOverridenLoc(Init)); assert(IFD->getChainingSize() >= 2); unsigned NestedFieldOffset = 0; @@ -6118,6 +6196,8 @@ bool Compiler<Emitter>::compileDestructor(const CXXDestructorDecl *Dtor) { assert(R); if (!R->isUnion()) { + + LocOverrideScope<Emitter> LOS(this, SourceInfo{}); // First, destroy all fields. for (const Record::Field &Field : llvm::reverse(R->fields())) { const Descriptor *D = Field.Desc; diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index 16f108f..ee8327d 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -40,6 +40,7 @@ template <class Emitter> class LoopScope; template <class Emitter> class LabelScope; template <class Emitter> class SwitchScope; template <class Emitter> class StmtExprScope; +template <class Emitter> class LocOverrideScope; template <class Emitter> class Compiler; struct InitLink { @@ -333,6 +334,7 @@ private: friend class LabelScope<Emitter>; friend class SwitchScope<Emitter>; friend class StmtExprScope<Emitter>; + friend class LocOverrideScope<Emitter>; /// Emits a zero initializer. bool visitZeroInitializer(PrimType T, QualType QT, const Expr *E); diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp index aaeb52e..f7f528c 100644 --- a/clang/lib/AST/ByteCode/Context.cpp +++ b/clang/lib/AST/ByteCode/Context.cpp @@ -15,6 +15,7 @@ #include "InterpStack.h" #include "PrimType.h" #include "Program.h" +#include "clang/AST/ASTLambda.h" #include "clang/AST/Expr.h" #include "clang/Basic/TargetInfo.h" @@ -44,12 +45,12 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) { Compiler<ByteCodeEmitter>(*this, *P).compileFunc( FD, const_cast<Function *>(Func)); - ++EvalID; - // And run it. - if (!Run(Parent, Func)) + if (!Func->isValid()) return false; - return Func->isValid(); + ++EvalID; + // And run it. + return Run(Parent, Func); } void Context::isPotentialConstantExprUnevaluated(State &Parent, const Expr *E, @@ -473,7 +474,7 @@ const Function *Context::getOrCreateFunction(const FunctionDecl *FuncDecl) { IsLambdaStaticInvoker = true; const CXXRecordDecl *ClosureClass = MD->getParent(); - assert(ClosureClass->captures_begin() == ClosureClass->captures_end()); + assert(ClosureClass->captures().empty()); if (ClosureClass->isGenericLambda()) { const CXXMethodDecl *LambdaCallOp = ClosureClass->getLambdaCallOperator(); assert(MD->isFunctionTemplateSpecialization() && diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h index 62ef529..1c084ac 100644 --- a/clang/lib/AST/ByteCode/Context.h +++ b/clang/lib/AST/ByteCode/Context.h @@ -17,9 +17,9 @@ #define LLVM_CLANG_AST_INTERP_CONTEXT_H #include "InterpStack.h" +#include "clang/AST/ASTContext.h" namespace clang { -class ASTContext; class LangOptions; class FunctionDecl; class VarDecl; diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp index 5b9f445..7403e90 100644 --- a/clang/lib/AST/ByteCode/Descriptor.cpp +++ b/clang/lib/AST/ByteCode/Descriptor.cpp @@ -21,14 +21,31 @@ using namespace clang; using namespace clang::interp; +template <typename T> static constexpr bool needsCtor() { + if constexpr (std::is_same_v<T, Integral<8, true>> || + std::is_same_v<T, Integral<8, false>> || + std::is_same_v<T, Integral<16, true>> || + std::is_same_v<T, Integral<16, false>> || + std::is_same_v<T, Integral<32, true>> || + std::is_same_v<T, Integral<32, false>> || + std::is_same_v<T, Integral<64, true>> || + std::is_same_v<T, Integral<64, false>> || + std::is_same_v<T, Boolean>) + return false; + + return true; +} + template <typename T> static void ctorTy(Block *, std::byte *Ptr, bool, bool, bool, bool, bool, const Descriptor *) { + static_assert(needsCtor<T>()); new (Ptr) T(); } template <typename T> static void dtorTy(Block *, std::byte *Ptr, const Descriptor *) { + static_assert(needsCtor<T>()); reinterpret_cast<T *>(Ptr)->~T(); } @@ -45,9 +62,11 @@ static void ctorArrayTy(Block *, std::byte *Ptr, bool, bool, bool, bool, bool, const Descriptor *D) { new (Ptr) InitMapPtr(std::nullopt); - Ptr += sizeof(InitMapPtr); - for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { - new (&reinterpret_cast<T *>(Ptr)[I]) T(); + if constexpr (needsCtor<T>()) { + Ptr += sizeof(InitMapPtr); + for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { + new (&reinterpret_cast<T *>(Ptr)[I]) T(); + } } } @@ -57,9 +76,12 @@ static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) { if (IMP) IMP = std::nullopt; - Ptr += sizeof(InitMapPtr); - for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { - reinterpret_cast<T *>(Ptr)[I].~T(); + + if constexpr (needsCtor<T>()) { + Ptr += sizeof(InitMapPtr); + for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { + reinterpret_cast<T *>(Ptr)[I].~T(); + } } } @@ -74,10 +96,14 @@ static void moveArrayTy(Block *, std::byte *Src, std::byte *Dst, } Src += sizeof(InitMapPtr); Dst += sizeof(InitMapPtr); - 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)); + if constexpr (!needsCtor<T>()) { + std::memcpy(Dst, Src, D->getNumElems() * D->getElemSize()); + } else { + 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)); + } } } diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp index 81ebc56..976b7c0 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.cpp +++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp @@ -53,6 +53,7 @@ EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD, bool CheckFullyInitialized) { this->CheckFullyInitialized = CheckFullyInitialized; S.EvaluatingDecl = VD; + S.setEvalLocation(VD->getLocation()); EvalResult.setSource(VD); if (const Expr *Init = VD->getAnyInitializer()) { @@ -233,6 +234,14 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) { return false; } } else { + // If this is pointing to a local variable, just return + // the result, even if the pointer is dead. + // This will later be diagnosed by CheckLValueConstantExpression. + if (Ptr.isBlockPointer() && !Ptr.block()->isStatic()) { + EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext())); + return true; + } + if (!Ptr.isLive() && !Ptr.isTemporary()) return false; @@ -282,6 +291,10 @@ bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) { using T = typename PrimConv<OpType>::T; Block *B = getLocal(I); + + if (!CheckLocalLoad(S, OpPC, Pointer(B))) + return false; + S.Stk.push<T>(*reinterpret_cast<T *>(B->data())); return true; } diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h b/clang/lib/AST/ByteCode/EvalEmitter.h index 2fe7da6..85a0a99 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.h +++ b/clang/lib/AST/ByteCode/EvalEmitter.h @@ -95,6 +95,7 @@ protected: ParamOffset LambdaThisCapture{0, false}; /// Local descriptors. llvm::SmallVector<SmallVector<Local, 8>, 2> Descriptors; + std::optional<SourceInfo> LocOverride = std::nullopt; private: /// Current compilation context. diff --git a/clang/lib/AST/ByteCode/Function.cpp b/clang/lib/AST/ByteCode/Function.cpp index 0e639df3..a513be5 100644 --- a/clang/lib/AST/ByteCode/Function.cpp +++ b/clang/lib/AST/ByteCode/Function.cpp @@ -8,6 +8,7 @@ #include "Function.h" #include "Program.h" +#include "clang/AST/ASTLambda.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" diff --git a/clang/lib/AST/ByteCode/Function.h b/clang/lib/AST/ByteCode/Function.h index de88f3d..92363b6 100644 --- a/clang/lib/AST/ByteCode/Function.h +++ b/clang/lib/AST/ByteCode/Function.h @@ -17,9 +17,9 @@ #include "Descriptor.h" #include "Source.h" -#include "clang/AST/ASTLambda.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/Support/raw_ostream.h" @@ -236,7 +236,7 @@ private: bool HasRVO, bool IsLambdaStaticInvoker); /// Sets the code of a function. - void setCode(unsigned NewFrameSize, std::vector<std::byte> &&NewCode, + void setCode(unsigned NewFrameSize, llvm::SmallVector<std::byte> &&NewCode, SourceMap &&NewSrcMap, llvm::SmallVector<Scope, 2> &&NewScopes, bool NewHasBody) { FrameSize = NewFrameSize; @@ -266,7 +266,7 @@ private: /// Size of the argument stack. unsigned ArgSize; /// Program code. - std::vector<std::byte> Code; + llvm::SmallVector<std::byte> Code; /// Opcode-to-expression mapping. SourceMap SrcMap; /// List of block descriptors. diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 224d65c..eb4e480 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -715,23 +715,6 @@ static bool CheckLifetime(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return false; } -bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - if (Ptr.isInitialized()) - return true; - - assert(S.getLangOpts().CPlusPlus); - const auto *VD = cast<VarDecl>(Ptr.getDeclDesc()->asValueDecl()); - if ((!VD->hasConstantInitialization() && - VD->mightBeUsableInConstantExpressions(S.getASTContext())) || - (S.getLangOpts().OpenCL && !S.getLangOpts().CPlusPlus11 && - !VD->hasICEInitializer(S.getASTContext()))) { - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_constexpr_var_init_non_constant, 1) << VD; - S.Note(VD->getLocation(), diag::note_declared_at); - } - return false; -} - static bool CheckWeak(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { if (!Ptr.isWeak()) return true; @@ -745,6 +728,39 @@ static bool CheckWeak(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { return false; } +// The list of checks here is just the one from CheckLoad, but with the +// ones removed that are impossible on primitive global values. +// For example, since those can't be members of structs, they also can't +// be mutable. +bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (!CheckExtern(S, OpPC, Ptr)) + return false; + if (!CheckConstant(S, OpPC, Ptr)) + return false; + if (!CheckDummy(S, OpPC, Ptr, AK_Read)) + return false; + if (!CheckInitialized(S, OpPC, Ptr, AK_Read)) + return false; + if (!CheckTemporary(S, OpPC, Ptr, AK_Read)) + return false; + if (!CheckWeak(S, OpPC, Ptr)) + return false; + if (!CheckVolatile(S, OpPC, Ptr, AK_Read)) + return false; + return true; +} + +// Similarly, for local loads. +bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + if (!CheckLifetime(S, OpPC, Ptr, AK_Read)) + return false; + if (!CheckInitialized(S, OpPC, Ptr, AK_Read)) + return false; + if (!CheckVolatile(S, OpPC, Ptr, AK_Read)) + return false; + return true; +} + bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK) { if (!CheckLive(S, OpPC, Ptr, AK)) diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 9a325ab..8a28106 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -91,8 +91,9 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr); bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK); -/// Check if a global variable is initialized. -bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr); +/// Checks a direct load of a primitive value from a global or local variable. +bool CheckGlobalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr); +bool CheckLocalLoad(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); @@ -1351,7 +1352,7 @@ inline bool ConstFloat(InterpState &S, CodePtr OpPC, const Floating &F) { template <PrimType Name, class T = typename PrimConv<Name>::T> bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { const Pointer &Ptr = S.Current->getLocalPointer(I); - if (!CheckLoad(S, OpPC, Ptr)) + if (!CheckLocalLoad(S, OpPC, Ptr)) return false; S.Stk.push<T>(Ptr.deref<T>()); return true; @@ -1465,14 +1466,8 @@ bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) { template <PrimType Name, class T = typename PrimConv<Name>::T> bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { const Pointer &Ptr = S.P.getPtrGlobal(I); - if (!CheckConstant(S, OpPC, Ptr.getFieldDesc())) - return false; - if (Ptr.isExtern()) - return false; - // If a global variable is uninitialized, that means the initializer we've - // compiled for it wasn't a constant expression. Diagnose that. - if (!CheckGlobalInitialized(S, OpPC, Ptr)) + if (!CheckGlobalLoad(S, OpPC, Ptr)) return false; S.Stk.push<T>(Ptr.deref<T>()); diff --git a/clang/lib/AST/ByteCode/InterpFrame.cpp b/clang/lib/AST/ByteCode/InterpFrame.cpp index d62a4f6..9342192 100644 --- a/clang/lib/AST/ByteCode/InterpFrame.cpp +++ b/clang/lib/AST/ByteCode/InterpFrame.cpp @@ -133,6 +133,11 @@ static bool shouldSkipInBacktrace(const Function *F) { MD && MD->getParent()->isAnonymousStructOrUnion()) return true; + if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FD); + Ctor && Ctor->isDefaulted() && Ctor->isTrivial() && + Ctor->isCopyOrMoveConstructor() && Ctor->inits().empty()) + return true; + return false; } @@ -202,7 +207,17 @@ SourceRange InterpFrame::getCallRange() const { return NullRange; return S.EvalLocation; } - return S.getRange(Caller->Func, RetPC - sizeof(uintptr_t)); + + // Move up to the frame that has a valid location for the caller. + for (const InterpFrame *C = this; C; C = C->Caller) { + if (!C->RetPC) + continue; + SourceRange CallRange = + S.getRange(C->Caller->Func, C->RetPC - sizeof(uintptr_t)); + if (CallRange.isValid()) + return CallRange; + } + return S.EvalLocation; } const FunctionDecl *InterpFrame::getCallee() const { diff --git a/clang/lib/AST/ByteCode/InterpShared.cpp b/clang/lib/AST/ByteCode/InterpShared.cpp index 1e94dc1..e01b32b 100644 --- a/clang/lib/AST/ByteCode/InterpShared.cpp +++ b/clang/lib/AST/ByteCode/InterpShared.cpp @@ -16,23 +16,23 @@ namespace interp { llvm::BitVector collectNonNullArgs(const FunctionDecl *F, ArrayRef<const Expr *> Args) { llvm::BitVector NonNullArgs; - if (!F) - return NonNullArgs; assert(F); + assert(F->hasAttr<NonNullAttr>()); NonNullArgs.resize(Args.size()); for (const auto *Attr : F->specific_attrs<NonNullAttr>()) { if (!Attr->args_size()) { NonNullArgs.set(); break; - } else - for (auto Idx : Attr->args()) { - unsigned ASTIdx = Idx.getASTIndex(); - if (ASTIdx >= Args.size()) - continue; - NonNullArgs[ASTIdx] = true; - } + } + + for (auto Idx : Attr->args()) { + unsigned ASTIdx = Idx.getASTIndex(); + if (ASTIdx >= Args.size()) + continue; + NonNullArgs[ASTIdx] = true; + } } return NonNullArgs; diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp index 3010847..a06b125 100644 --- a/clang/lib/AST/ByteCode/InterpState.cpp +++ b/clang/lib/AST/ByteCode/InterpState.cpp @@ -11,6 +11,8 @@ #include "InterpStack.h" #include "Program.h" #include "State.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" using namespace clang; using namespace clang::interp; @@ -77,27 +79,27 @@ void InterpState::deallocate(Block *B) { const Descriptor *Desc = B->getDescriptor(); assert(Desc); + // The block might have a pointer saved in a field in its data + // that points to the block itself. We call the dtor first, + // which will destroy all the data but leave InlineDescriptors + // intact. If the block THEN still has pointers, we create a + // DeadBlock for it. + if (B->IsInitialized) + B->invokeDtor(); + if (B->hasPointers()) { size_t Size = B->getSize(); - // Allocate a new block, transferring over pointers. char *Memory = reinterpret_cast<char *>(std::malloc(sizeof(DeadBlock) + Size)); auto *D = new (Memory) DeadBlock(DeadBlocks, B); - std::memset(D->B.rawData(), 0, D->B.getSize()); - - // Move data and metadata from the old block to the new (dead)block. - if (B->IsInitialized && Desc->MoveFn) { - Desc->MoveFn(B, B->data(), D->data(), Desc); - if (Desc->getMetadataSize() > 0) - std::memcpy(D->rawData(), B->rawData(), Desc->getMetadataSize()); - } + // Since the block doesn't hold any actual data anymore, we can just + // memcpy() everything over. + std::memcpy(D->rawData(), B->rawData(), Desc->getAllocSize()); D->B.IsInitialized = B->IsInitialized; // We moved the contents over to the DeadBlock. B->IsInitialized = false; - } else if (B->IsInitialized) { - B->invokeDtor(); } } diff --git a/clang/lib/AST/ByteCode/PrimType.h b/clang/lib/AST/ByteCode/PrimType.h index 38c29b9..724da93 100644 --- a/clang/lib/AST/ByteCode/PrimType.h +++ b/clang/lib/AST/ByteCode/PrimType.h @@ -13,7 +13,6 @@ #ifndef LLVM_CLANG_AST_INTERP_TYPE_H #define LLVM_CLANG_AST_INTERP_TYPE_H -#include "clang/Basic/UnsignedOrNone.h" #include "llvm/Support/raw_ostream.h" #include <climits> #include <cstddef> diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp index 2421ec4..4daa4ab 100644 --- a/clang/lib/AST/ByteCode/Program.cpp +++ b/clang/lib/AST/ByteCode/Program.cpp @@ -13,6 +13,7 @@ #include "PrimType.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" using namespace clang; using namespace clang::interp; diff --git a/clang/lib/AST/ByteCode/State.cpp b/clang/lib/AST/ByteCode/State.cpp index 3204d1a..dc3d0da 100644 --- a/clang/lib/AST/ByteCode/State.cpp +++ b/clang/lib/AST/ByteCode/State.cpp @@ -131,6 +131,7 @@ void State::addCallStack(unsigned Limit) { const Frame *Bottom = getBottomFrame(); for (const Frame *F = Top; F != Bottom; F = F->getCaller(), ++CallIdx) { SourceRange CallRange = F->getCallRange(); + assert(CallRange.isValid()); // Skip this call? if (CallIdx >= SkipStart && CallIdx < SkipEnd) { |