diff options
Diffstat (limited to 'clang/lib/AST/ByteCode')
24 files changed, 863 insertions, 603 deletions
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index e3235d34e230..a21358338250 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -62,7 +62,7 @@ public: OptionScope(Compiler<Emitter> *Ctx, bool NewDiscardResult, bool NewInitializing, bool NewToLValue) : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), - OldInitializing(Ctx->Initializing), OldToLValue(NewToLValue) { + OldInitializing(Ctx->Initializing), OldToLValue(Ctx->ToLValue) { Ctx->DiscardResult = NewDiscardResult; Ctx->Initializing = NewInitializing; Ctx->ToLValue = NewToLValue; @@ -559,8 +559,7 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) { // Possibly diagnose casts to enum types if the target type does not // have a fixed size. if (Ctx.getLangOpts().CPlusPlus && CE->getType()->isEnumeralType()) { - const auto *ET = CE->getType().getCanonicalType()->castAs<EnumType>(); - const auto *ED = ET->getOriginalDecl()->getDefinitionOrSelf(); + const auto *ED = CE->getType()->castAsEnumDecl(); if (!ED->isFixed()) { if (!this->emitCheckEnumValue(*FromT, ED, CE)) return false; @@ -943,7 +942,7 @@ bool Compiler<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) { if (!Result) return false; if (DiscardResult) - return this->emitPop(*T, BO); + return this->emitPopBool(BO); if (T != PT_Bool) return this->emitCast(PT_Bool, *T, BO); return true; @@ -1377,14 +1376,20 @@ bool Compiler<Emitter>::VisitComplexBinOp(const BinaryOperator *E) { template <class Emitter> bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) { + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); assert(!E->isCommaOp() && "Comma op should be handled in VisitBinaryOperator"); assert(E->getType()->isVectorType()); - assert(E->getLHS()->getType()->isVectorType()); - assert(E->getRHS()->getType()->isVectorType()); + assert(LHS->getType()->isVectorType()); + assert(RHS->getType()->isVectorType()); + + // We can only handle vectors with primitive element types. + if (!canClassify(LHS->getType()->castAs<VectorType>()->getElementType())) + return false; // Prepare storage for result. - if (!Initializing && !E->isCompoundAssignmentOp()) { + if (!Initializing && !E->isCompoundAssignmentOp() && !E->isAssignmentOp()) { UnsignedOrNone LocalIndex = allocateTemporary(E); if (!LocalIndex) return false; @@ -1392,8 +1397,6 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) { return false; } - const Expr *LHS = E->getLHS(); - const Expr *RHS = E->getRHS(); const auto *VecTy = E->getType()->getAs<VectorType>(); auto Op = E->isCompoundAssignmentOp() ? BinaryOperator::getOpForCompoundAssignment(E->getOpcode()) @@ -1403,6 +1406,21 @@ bool Compiler<Emitter>::VisitVectorBinOp(const BinaryOperator *E) { PrimType RHSElemT = this->classifyVectorElementType(RHS->getType()); PrimType ResultElemT = this->classifyVectorElementType(E->getType()); + if (E->getOpcode() == BO_Assign) { + assert(Ctx.getASTContext().hasSameUnqualifiedType( + LHS->getType()->castAs<VectorType>()->getElementType(), + RHS->getType()->castAs<VectorType>()->getElementType())); + if (!this->visit(LHS)) + return false; + if (!this->visit(RHS)) + return false; + if (!this->emitCopyArray(ElemT, 0, 0, VecTy->getNumElements(), E)) + return false; + if (DiscardResult) + return this->emitPopPtr(E); + return true; + } + // Evaluate LHS and save value to LHSOffset. unsigned LHSOffset = this->allocateLocalPrimitive(LHS, PT_Ptr, /*IsConst=*/true); @@ -2248,7 +2266,9 @@ bool Compiler<Emitter>::VisitUnaryExprOrTypeTraitExpr( assert(VAT); if (VAT->getElementType()->isArrayType()) { std::optional<APSInt> Res = - VAT->getSizeExpr()->getIntegerConstantExpr(ASTCtx); + VAT->getSizeExpr() + ? VAT->getSizeExpr()->getIntegerConstantExpr(ASTCtx) + : std::nullopt; if (Res) { if (DiscardResult) return true; @@ -2892,25 +2912,20 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr( OptPrimType SubExprT = classify(SubExpr); bool IsStatic = E->getStorageDuration() == SD_Static; if (IsStatic) { - std::optional<unsigned> GlobalIndex = P.createGlobal(E); + + UnsignedOrNone GlobalIndex = P.createGlobal(E); if (!GlobalIndex) return false; const LifetimeExtendedTemporaryDecl *TempDecl = E->getLifetimeExtendedTemporaryDecl(); - if (IsStatic) - assert(TempDecl); + assert(TempDecl); if (SubExprT) { if (!this->visit(SubExpr)) return false; - if (IsStatic) { - if (!this->emitInitGlobalTemp(*SubExprT, *GlobalIndex, TempDecl, E)) - return false; - } else { - if (!this->emitInitGlobal(*SubExprT, *GlobalIndex, E)) - return false; - } + if (!this->emitInitGlobalTemp(*SubExprT, *GlobalIndex, TempDecl, E)) + return false; return this->emitGetPtrGlobal(*GlobalIndex, E); } @@ -2921,9 +2936,7 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr( return false; if (!this->visitInitializer(SubExpr)) return false; - if (IsStatic) - return this->emitInitGlobalTempComp(TempDecl, E); - return true; + return this->emitInitGlobalTempComp(TempDecl, E); } // For everyhing else, use local variables. @@ -2989,7 +3002,7 @@ bool Compiler<Emitter>::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) { if (T && !E->isLValue()) return this->delegate(Init); - std::optional<unsigned> GlobalIndex = P.createGlobal(E); + UnsignedOrNone GlobalIndex = P.createGlobal(E); if (!GlobalIndex) return false; @@ -3333,7 +3346,7 @@ bool Compiler<Emitter>::VisitSourceLocExpr(const SourceLocExpr *E) { auto *UGCD = cast<UnnamedGlobalConstantDecl>(BaseDecl); - std::optional<unsigned> GlobalIndex = P.getOrCreateGlobal(UGCD); + UnsignedOrNone GlobalIndex = P.getOrCreateGlobal(UGCD); if (!GlobalIndex) return false; @@ -3856,7 +3869,7 @@ bool Compiler<Emitter>::VisitCXXUuidofExpr(const CXXUuidofExpr *E) { if (!RD->isCompleteDefinition()) return this->emitDummyPtr(GuidDecl, E); - std::optional<unsigned> GlobalIndex = P.getOrCreateGlobal(GuidDecl); + UnsignedOrNone GlobalIndex = P.getOrCreateGlobal(GuidDecl); if (!GlobalIndex) return false; if (!this->emitGetPtrGlobal(*GlobalIndex, E)) @@ -3879,6 +3892,8 @@ bool Compiler<Emitter>::VisitCXXUuidofExpr(const CXXUuidofExpr *E) { template <class Emitter> bool Compiler<Emitter>::VisitRequiresExpr(const RequiresExpr *E) { assert(classifyPrim(E->getType()) == PT_Bool); + if (E->isValueDependent()) + return false; if (DiscardResult) return true; return this->emitConstBool(E->isSatisfied(), E); @@ -4169,6 +4184,31 @@ template <class Emitter> bool Compiler<Emitter>::delegate(const Expr *E) { return this->Visit(E); } +static const Expr *stripCheckedDerivedToBaseCasts(const Expr *E) { + if (const auto *PE = dyn_cast<ParenExpr>(E)) + return stripCheckedDerivedToBaseCasts(PE->getSubExpr()); + + if (const auto *CE = dyn_cast<CastExpr>(E); + CE && + (CE->getCastKind() == CK_DerivedToBase || CE->getCastKind() == CK_NoOp)) + return stripCheckedDerivedToBaseCasts(CE->getSubExpr()); + + return E; +} + +static const Expr *stripDerivedToBaseCasts(const Expr *E) { + if (const auto *PE = dyn_cast<ParenExpr>(E)) + return stripDerivedToBaseCasts(PE->getSubExpr()); + + if (const auto *CE = dyn_cast<CastExpr>(E); + CE && (CE->getCastKind() == CK_DerivedToBase || + CE->getCastKind() == CK_UncheckedDerivedToBase || + CE->getCastKind() == CK_NoOp)) + return stripDerivedToBaseCasts(CE->getSubExpr()); + + return E; +} + template <class Emitter> bool Compiler<Emitter>::visit(const Expr *E) { if (E->getType().isNull()) return false; @@ -4177,9 +4217,8 @@ template <class Emitter> bool Compiler<Emitter>::visit(const Expr *E) { return this->discard(E); // Create local variable to hold the return value. - if (!E->isGLValue() && !E->getType()->isAnyComplexType() && - !canClassify(E->getType())) { - UnsignedOrNone LocalIndex = allocateLocal(E); + if (!E->isGLValue() && !canClassify(E->getType())) { + UnsignedOrNone LocalIndex = allocateLocal(stripDerivedToBaseCasts(E)); if (!LocalIndex) return false; @@ -4608,8 +4647,8 @@ UnsignedOrNone Compiler<Emitter>::allocateTemporary(const Expr *E) { template <class Emitter> const RecordType *Compiler<Emitter>::getRecordTy(QualType Ty) { if (const PointerType *PT = dyn_cast<PointerType>(Ty)) - return PT->getPointeeType()->getAs<RecordType>(); - return Ty->getAs<RecordType>(); + return PT->getPointeeType()->getAsCanonical<RecordType>(); + return Ty->getAsCanonical<RecordType>(); } template <class Emitter> Record *Compiler<Emitter>::getRecord(QualType Ty) { @@ -4822,7 +4861,7 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD, DeclScope<Emitter> LocalScope(this, VD); // We've already seen and initialized this global. - if (std::optional<unsigned> GlobalIndex = P.getGlobal(VD)) { + if (UnsignedOrNone GlobalIndex = P.getGlobal(VD)) { if (P.getPtrGlobal(*GlobalIndex).isInitialized()) return checkDecl(); @@ -4831,7 +4870,7 @@ VarCreationState Compiler<Emitter>::visitVarDecl(const VarDecl *VD, return Init && checkDecl() && initGlobal(*GlobalIndex); } - std::optional<unsigned> GlobalIndex = P.createGlobal(VD, Init); + UnsignedOrNone GlobalIndex = P.createGlobal(VD, Init); if (!GlobalIndex) return false; @@ -5057,18 +5096,6 @@ bool Compiler<Emitter>::VisitBuiltinCallExpr(const CallExpr *E, return true; } -static const Expr *stripDerivedToBaseCasts(const Expr *E) { - if (const auto *PE = dyn_cast<ParenExpr>(E)) - return stripDerivedToBaseCasts(PE->getSubExpr()); - - if (const auto *CE = dyn_cast<CastExpr>(E); - CE && - (CE->getCastKind() == CK_DerivedToBase || CE->getCastKind() == CK_NoOp)) - return stripDerivedToBaseCasts(CE->getSubExpr()); - - return E; -} - template <class Emitter> bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) { const FunctionDecl *FuncDecl = E->getDirectCallee(); @@ -5173,7 +5200,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) { const auto *InstancePtr = MC->getImplicitObjectArgument(); if (isa_and_nonnull<CXXDestructorDecl>(CompilingFunction) || isa_and_nonnull<CXXConstructorDecl>(CompilingFunction)) { - const auto *Stripped = stripDerivedToBaseCasts(InstancePtr); + const auto *Stripped = stripCheckedDerivedToBaseCasts(InstancePtr); if (isa<CXXThisExpr>(Stripped)) { FuncDecl = cast<CXXMethodDecl>(FuncDecl)->getCorrespondingMethodInClass( @@ -5228,6 +5255,12 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) { const Function *Func = getFunction(FuncDecl); if (!Func) return false; + + // In error cases, the function may be called with fewer arguments than + // parameters. + if (E->getNumArgs() < Func->getNumWrittenParams()) + return false; + assert(HasRVO == Func->hasRVO()); bool HasQualifier = false; @@ -6244,26 +6277,21 @@ bool Compiler<Emitter>::compileDestructor(const CXXDestructorDecl *Dtor) { // First, destroy all fields. for (const Record::Field &Field : llvm::reverse(R->fields())) { const Descriptor *D = Field.Desc; - if (!D->isPrimitive() && !D->isPrimitiveArray()) { - if (!this->emitGetPtrField(Field.Offset, SourceInfo{})) - return false; - if (!this->emitDestruction(D, SourceInfo{})) - return false; - if (!this->emitPopPtr(SourceInfo{})) - return false; - } + if (D->hasTrivialDtor()) + continue; + if (!this->emitGetPtrField(Field.Offset, SourceInfo{})) + return false; + if (!this->emitDestructionPop(D, SourceInfo{})) + return false; } } for (const Record::Base &Base : llvm::reverse(R->bases())) { - if (Base.R->isAnonymousUnion()) + if (Base.R->hasTrivialDtor()) continue; - if (!this->emitGetPtrBase(Base.Offset, SourceInfo{})) return false; - if (!this->emitRecordDestruction(Base.R, {})) - return false; - if (!this->emitPopPtr(SourceInfo{})) + if (!this->emitRecordDestructionPop(Base.R, {})) return false; } @@ -6775,7 +6803,7 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) { return F && this->emitGetFnPtr(F, E); } if (const auto *TPOD = dyn_cast<TemplateParamObjectDecl>(D)) { - if (std::optional<unsigned> Index = P.getOrCreateGlobal(D)) { + if (UnsignedOrNone Index = P.getOrCreateGlobal(D)) { if (!this->emitGetPtrGlobal(*Index, E)) return false; if (OptPrimType T = classify(E->getType())) { @@ -7160,71 +7188,56 @@ bool Compiler<Emitter>::emitComplexComparison(const Expr *LHS, const Expr *RHS, /// on the stack. /// Emit destruction of record types (or arrays of record types). template <class Emitter> -bool Compiler<Emitter>::emitRecordDestruction(const Record *R, SourceInfo Loc) { +bool Compiler<Emitter>::emitRecordDestructionPop(const Record *R, + SourceInfo Loc) { assert(R); - assert(!R->isAnonymousUnion()); + assert(!R->hasTrivialDtor()); const CXXDestructorDecl *Dtor = R->getDestructor(); - if (!Dtor || Dtor->isTrivial()) - return true; - assert(Dtor); const Function *DtorFunc = getFunction(Dtor); if (!DtorFunc) return false; assert(DtorFunc->hasThisPointer()); assert(DtorFunc->getNumParams() == 1); - if (!this->emitDupPtr(Loc)) - return false; return this->emitCall(DtorFunc, 0, Loc); } /// When calling this, we have a pointer of the local-to-destroy /// on the stack. /// Emit destruction of record types (or arrays of record types). template <class Emitter> -bool Compiler<Emitter>::emitDestruction(const Descriptor *Desc, - SourceInfo Loc) { +bool Compiler<Emitter>::emitDestructionPop(const Descriptor *Desc, + SourceInfo Loc) { assert(Desc); - assert(!Desc->isPrimitive()); - assert(!Desc->isPrimitiveArray()); + assert(!Desc->hasTrivialDtor()); // Arrays. if (Desc->isArray()) { const Descriptor *ElemDesc = Desc->ElemDesc; assert(ElemDesc); - // Don't need to do anything for these. - if (ElemDesc->isPrimitiveArray()) - return true; + unsigned N = Desc->getNumElems(); + if (N == 0) + return this->emitPopPtr(Loc); - // If this is an array of record types, check if we need - // to call the element destructors at all. If not, try - // to save the work. - if (const Record *ElemRecord = ElemDesc->ElemRecord) { - if (const CXXDestructorDecl *Dtor = ElemRecord->getDestructor(); - !Dtor || Dtor->isTrivial()) - return true; - } - - if (unsigned N = Desc->getNumElems()) { - for (ssize_t I = N - 1; I >= 0; --I) { - if (!this->emitConstUint64(I, Loc)) - return false; - if (!this->emitArrayElemPtrUint64(Loc)) - return false; - if (!this->emitDestruction(ElemDesc, Loc)) - return false; - if (!this->emitPopPtr(Loc)) - return false; - } + for (ssize_t I = N - 1; I >= 1; --I) { + if (!this->emitConstUint64(I, Loc)) + return false; + if (!this->emitArrayElemPtrUint64(Loc)) + return false; + if (!this->emitDestructionPop(ElemDesc, Loc)) + return false; } - return true; + // Last iteration, removes the instance pointer from the stack. + if (!this->emitConstUint64(0, Loc)) + return false; + if (!this->emitArrayElemPtrPopUint64(Loc)) + return false; + return this->emitDestructionPop(ElemDesc, Loc); } assert(Desc->ElemRecord); - if (Desc->ElemRecord->isAnonymousUnion()) - return true; - - return this->emitRecordDestruction(Desc->ElemRecord, Loc); + assert(!Desc->ElemRecord->hasTrivialDtor()); + return this->emitRecordDestructionPop(Desc->ElemRecord, Loc); } /// Create a dummy pointer for the given decl (or expr) and diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index 475faee4d3fd..bb8c66042731 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -391,8 +391,8 @@ private: bool emitComplexBoolCast(const Expr *E); bool emitComplexComparison(const Expr *LHS, const Expr *RHS, const BinaryOperator *E); - bool emitRecordDestruction(const Record *R, SourceInfo Loc); - bool emitDestruction(const Descriptor *Desc, SourceInfo Loc); + bool emitRecordDestructionPop(const Record *R, SourceInfo Loc); + bool emitDestructionPop(const Descriptor *Desc, SourceInfo Loc); bool emitDummyPtr(const DeclTy &D, const Expr *E); bool emitFloat(const APFloat &F, const Expr *E); unsigned collectBaseOffset(const QualType BaseType, @@ -587,11 +587,9 @@ public: if (!this->Ctx->emitGetPtrLocal(Local.Offset, E)) return false; - if (!this->Ctx->emitDestruction(Local.Desc, Local.Desc->getLoc())) + if (!this->Ctx->emitDestructionPop(Local.Desc, Local.Desc->getLoc())) return false; - if (!this->Ctx->emitPopPtr(E)) - return false; removeIfStoredOpaqueValue(Local); } return true; diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp index 36eb7607e70b..859899668146 100644 --- a/clang/lib/AST/ByteCode/Context.cpp +++ b/clang/lib/AST/ByteCode/Context.cpp @@ -91,7 +91,7 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) { #endif } - Result = Res.toAPValue(); + Result = Res.stealAPValue(); return true; } @@ -121,7 +121,7 @@ bool Context::evaluate(State &Parent, const Expr *E, APValue &Result, #endif } - Result = Res.toAPValue(); + Result = Res.stealAPValue(); return true; } @@ -153,7 +153,7 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, #endif } - Result = Res.toAPValue(); + Result = Res.stealAPValue(); return true; } @@ -364,8 +364,7 @@ OptPrimType Context::classify(QualType T) const { return integralTypeToPrimTypeU(BT->getNumBits()); } - if (const auto *ET = T->getAs<EnumType>()) { - const auto *D = ET->getOriginalDecl()->getDefinitionOrSelf(); + if (const auto *D = T->getAsEnumDecl()) { if (!D->isComplete()) return std::nullopt; return classify(D->getIntegerType()); diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp index 9ecc7b673cf2..0a819599287e 100644 --- a/clang/lib/AST/ByteCode/Descriptor.cpp +++ b/clang/lib/AST/ByteCode/Descriptor.cpp @@ -50,14 +50,6 @@ static void dtorTy(Block *, std::byte *Ptr, const Descriptor *) { } template <typename T> -static void moveTy(Block *, std::byte *Src, std::byte *Dst, - const 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 *, std::byte *Ptr, bool, bool, bool, bool, bool, const Descriptor *D) { new (Ptr) InitMapPtr(std::nullopt); @@ -85,28 +77,6 @@ static void dtorArrayTy(Block *, std::byte *Ptr, const Descriptor *D) { } } -template <typename T> -static void moveArrayTy(Block *, std::byte *Src, std::byte *Dst, - const Descriptor *D) { - InitMapPtr &SrcIMP = *reinterpret_cast<InitMapPtr *>(Src); - if (SrcIMP) { - // We only ever invoke the moveFunc when moving block contents to a - // DeadBlock. DeadBlocks don't need InitMaps, so we destroy them here. - SrcIMP = std::nullopt; - } - Src += sizeof(InitMapPtr); - Dst += sizeof(InitMapPtr); - 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)); - } - } -} - static void ctorArrayDesc(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable, bool IsVolatile, bool IsActive, bool InUnion, const Descriptor *D) { @@ -144,12 +114,14 @@ static void dtorArrayDesc(Block *B, std::byte *Ptr, const Descriptor *D) { D->ElemDesc->getAllocSize() + sizeof(InlineDescriptor); unsigned ElemOffset = 0; - for (unsigned I = 0; I < NumElems; ++I, ElemOffset += ElemSize) { + auto Dtor = D->ElemDesc->DtorFn; + assert(Dtor && + "a composite array without an elem dtor shouldn't have a dtor itself"); + for (unsigned I = 0; I != NumElems; ++I, ElemOffset += ElemSize) { auto *ElemPtr = Ptr + ElemOffset; auto *Desc = reinterpret_cast<InlineDescriptor *>(ElemPtr); auto *ElemLoc = reinterpret_cast<std::byte *>(Desc + 1); - if (auto Fn = D->ElemDesc->DtorFn) - Fn(B, ElemLoc, D->ElemDesc); + Dtor(B, ElemLoc, D->ElemDesc); } } @@ -246,34 +218,59 @@ static void dtorRecord(Block *B, std::byte *Ptr, const Descriptor *D) { destroyBase(B, Ptr, F.Desc, F.Offset); } -static BlockCtorFn getCtorPrim(PrimType Type) { - // Floating types are special. They are primitives, but need their - // constructor called. - if (Type == PT_Float) +/// Whether a record needs its descriptor dtor function called. +static bool needsRecordDtor(const Record *R) { + for (const auto &B : R->bases()) { + if (B.Desc->DtorFn) + return true; + } + + for (const auto &F : R->fields()) { + if (F.Desc->DtorFn) + return true; + } + + for (const auto &V : R->virtual_bases()) { + if (V.Desc->DtorFn) + return true; + } + return false; +} + +static BlockCtorFn getCtorPrim(PrimType T) { + switch (T) { + case PT_Float: return ctorTy<PrimConv<PT_Float>::T>; - if (Type == PT_IntAP) + case PT_IntAP: return ctorTy<PrimConv<PT_IntAP>::T>; - if (Type == PT_IntAPS) + case PT_IntAPS: return ctorTy<PrimConv<PT_IntAPS>::T>; - if (Type == PT_MemberPtr) + case PT_Ptr: + return ctorTy<PrimConv<PT_Ptr>::T>; + case PT_MemberPtr: return ctorTy<PrimConv<PT_MemberPtr>::T>; - - COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr); + default: + return nullptr; + } + llvm_unreachable("Unhandled PrimType"); } -static BlockDtorFn getDtorPrim(PrimType Type) { - // Floating types are special. They are primitives, but need their - // destructor called, since they might allocate memory. - if (Type == PT_Float) +static BlockDtorFn getDtorPrim(PrimType T) { + switch (T) { + case PT_Float: return dtorTy<PrimConv<PT_Float>::T>; - if (Type == PT_IntAP) + case PT_IntAP: return dtorTy<PrimConv<PT_IntAP>::T>; - if (Type == PT_IntAPS) + case PT_IntAPS: return dtorTy<PrimConv<PT_IntAPS>::T>; - if (Type == PT_MemberPtr) + case PT_Ptr: + return dtorTy<PrimConv<PT_Ptr>::T>; + case PT_MemberPtr: return dtorTy<PrimConv<PT_MemberPtr>::T>; - - COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr); + default: + return nullptr; + } + llvm_unreachable("Unhandled PrimType"); } static BlockCtorFn getCtorArrayPrim(PrimType Type) { @@ -336,7 +333,7 @@ Descriptor::Descriptor(const DeclTy &D, const Type *SourceTy, AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize), ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc), - DtorFn(dtorArrayDesc) { + DtorFn(Elem->DtorFn ? dtorArrayDesc : nullptr) { assert(Source && "Missing source"); } @@ -347,7 +344,7 @@ Descriptor::Descriptor(const DeclTy &D, const Descriptor *Elem, MetadataSize MD, Size(UnknownSizeMark), MDSize(MD.value_or(0)), AllocSize(MDSize + alignof(void *)), ElemDesc(Elem), IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), - CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc) { + CtorFn(ctorArrayDesc), DtorFn(Elem->DtorFn ? dtorArrayDesc : nullptr) { assert(Source && "Missing source"); } @@ -359,7 +356,7 @@ Descriptor::Descriptor(const DeclTy &D, const Record *R, MetadataSize MD, Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize), ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), IsVolatile(IsVolatile), CtorFn(ctorRecord), - DtorFn(dtorRecord) { + DtorFn(needsRecordDtor(R) ? dtorRecord : nullptr) { assert(Source && "Missing source"); } @@ -460,8 +457,7 @@ bool Descriptor::hasTrivialDtor() const { if (isRecord()) { assert(ElemRecord); - const CXXDestructorDecl *Dtor = ElemRecord->getDestructor(); - return !Dtor || Dtor->isTrivial(); + return ElemRecord->hasTrivialDtor(); } if (!ElemDesc) diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp index 58e6d1a722af..ab3b9f7c3b1d 100644 --- a/clang/lib/AST/ByteCode/Disasm.cpp +++ b/clang/lib/AST/ByteCode/Disasm.cpp @@ -44,7 +44,20 @@ inline static std::string printArg(Program &P, CodePtr &OpPC) { std::string Result; llvm::raw_string_ostream SS(Result); auto Arg = OpPC.read<T>(); - SS << Arg; + // Make sure we print the integral value of chars. + if constexpr (std::is_integral_v<T>) { + if constexpr (sizeof(T) == 1) { + if constexpr (std::is_signed_v<T>) + SS << static_cast<int32_t>(Arg); + else + SS << static_cast<uint32_t>(Arg); + } else { + SS << Arg; + } + } else { + SS << Arg; + } + return Result; } } @@ -549,41 +562,17 @@ LLVM_DUMP_METHOD void Block::dump(llvm::raw_ostream &OS) const { } LLVM_DUMP_METHOD void EvaluationResult::dump() const { - assert(Ctx); auto &OS = llvm::errs(); - const ASTContext &ASTCtx = Ctx->getASTContext(); - switch (Kind) { - case Empty: + if (empty()) { OS << "Empty\n"; - break; - case RValue: - OS << "RValue: "; - std::get<APValue>(Value).dump(OS, ASTCtx); - break; - case LValue: { - assert(Source); - QualType SourceType; - if (const auto *D = dyn_cast<const Decl *>(Source)) { - if (const auto *VD = dyn_cast<ValueDecl>(D)) - SourceType = VD->getType(); - } else if (const auto *E = dyn_cast<const Expr *>(Source)) { - SourceType = E->getType(); - } - - OS << "LValue: "; - if (const auto *P = std::get_if<Pointer>(&Value)) - P->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType); - else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope - FP->toAPValue(ASTCtx).printPretty(OS, ASTCtx, SourceType); - OS << "\n"; - break; - } - case Invalid: + } else if (isInvalid()) { OS << "Invalid\n"; - break; - case Valid: - OS << "Valid\n"; - break; + } else { + OS << "Value: "; +#ifndef NDEBUG + assert(Ctx); + Value.dump(OS, Ctx->getASTContext()); +#endif } } diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp index 1ebadae811bd..d0aa8d8df236 100644 --- a/clang/lib/AST/ByteCode/EvalEmitter.cpp +++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp @@ -184,7 +184,7 @@ template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) { return true; using T = typename PrimConv<OpType>::T; - EvalResult.setValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext())); + EvalResult.takeValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext())); return true; } @@ -195,7 +195,7 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (Ptr.isFunctionPointer()) { - EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext())); + EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext())); return true; } @@ -213,6 +213,9 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) { if (!Ptr.isZero() && !Ptr.isDereferencable()) return false; + if (Ptr.pointsToStringLiteral() && Ptr.isArrayRoot()) + return false; + if (!Ptr.isZero() && !CheckFinalLoad(S, OpPC, Ptr)) return false; @@ -224,7 +227,7 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) { if (std::optional<APValue> V = Ptr.toRValue(Ctx, EvalResult.getSourceType())) { - EvalResult.setValue(*V); + EvalResult.takeValue(std::move(*V)); } else { return false; } @@ -233,14 +236,14 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) { // 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())); + EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext())); return true; } if (!Ptr.isLive() && !Ptr.isTemporary()) return false; - EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext())); + EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext())); } return true; @@ -261,7 +264,7 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) { if (std::optional<APValue> APV = Ptr.toRValue(S.getASTContext(), EvalResult.getSourceType())) { - EvalResult.setValue(*APV); + EvalResult.takeValue(std::move(*APV)); return true; } @@ -328,7 +331,7 @@ bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) { /// This is what we do here. void EvalEmitter::updateGlobalTemporaries() { for (const auto &[E, Temp] : S.SeenGlobalTemporaries) { - if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) { + if (UnsignedOrNone GlobalIndex = P.getGlobal(E)) { const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex); APValue *Cached = Temp->getOrCreateValue(true); diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp index b11531f4296a..ba818788d702 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.cpp +++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp @@ -8,6 +8,7 @@ #include "EvaluationResult.h" #include "InterpState.h" +#include "Pointer.h" #include "Record.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" @@ -16,41 +17,6 @@ namespace clang { namespace interp { -APValue EvaluationResult::toAPValue() const { - assert(!empty()); - switch (Kind) { - case LValue: - // Either a pointer or a function pointer. - if (const auto *P = std::get_if<Pointer>(&Value)) - return P->toAPValue(Ctx->getASTContext()); - else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) - return FP->toAPValue(Ctx->getASTContext()); - else - llvm_unreachable("Unhandled LValue type"); - break; - case RValue: - return std::get<APValue>(Value); - case Valid: - return APValue(); - default: - llvm_unreachable("Unhandled result kind?"); - } -} - -std::optional<APValue> EvaluationResult::toRValue() const { - if (Kind == RValue) - return toAPValue(); - - assert(Kind == LValue); - - // We have a pointer and want an RValue. - if (const auto *P = std::get_if<Pointer>(&Value)) - return P->toRValue(*Ctx, getSourceType()); - else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope - return FP->toAPValue(Ctx->getASTContext()); - llvm_unreachable("Unhandled lvalue kind"); -} - static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc, const FieldDecl *SubObjDecl) { assert(SubObjDecl && "Subobject declaration does not exist"); @@ -66,8 +32,12 @@ static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc, const Pointer &BasePtr, const ConstantArrayType *CAT) { - bool Result = true; size_t NumElems = CAT->getZExtSize(); + + if (NumElems == 0) + return true; + + bool Result = true; QualType ElemType = CAT->getElementType(); if (ElemType->isRecordType()) { @@ -82,8 +52,18 @@ static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc, Result &= CheckArrayInitialized(S, Loc, ElemPtr, ElemCAT); } } else { + // Primitive arrays. + if (S.getContext().canClassify(ElemType)) { + if (BasePtr.allElementsInitialized()) { + return true; + } else { + DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField()); + return false; + } + } + for (size_t I = 0; I != NumElems; ++I) { - if (!BasePtr.atIndex(I).isInitialized()) { + if (!BasePtr.isElementInitialized(I)) { DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField()); Result = false; } @@ -178,8 +158,8 @@ bool EvaluationResult::checkFullyInitialized(InterpState &S, static void collectBlocks(const Pointer &Ptr, llvm::SetVector<const Block *> &Blocks) { auto isUsefulPtr = [](const Pointer &P) -> bool { - return P.isLive() && !P.isZero() && !P.isDummy() && P.isDereferencable() && - !P.isUnknownSizeArray() && !P.isOnePastEnd(); + return P.isLive() && P.isBlockPointer() && !P.isZero() && !P.isDummy() && + P.isDereferencable() && !P.isUnknownSizeArray() && !P.isOnePastEnd(); }; if (!isUsefulPtr(Ptr)) diff --git a/clang/lib/AST/ByteCode/EvaluationResult.h b/clang/lib/AST/ByteCode/EvaluationResult.h index 3b6c65eff1ef..c296cc98ca37 100644 --- a/clang/lib/AST/ByteCode/EvaluationResult.h +++ b/clang/lib/AST/ByteCode/EvaluationResult.h @@ -9,23 +9,22 @@ #ifndef LLVM_CLANG_AST_INTERP_EVALUATION_RESULT_H #define LLVM_CLANG_AST_INTERP_EVALUATION_RESULT_H -#include "FunctionPointer.h" -#include "Pointer.h" #include "clang/AST/APValue.h" #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" -#include <optional> -#include <variant> namespace clang { namespace interp { class EvalEmitter; class Context; +class Pointer; +class SourceInfo; +class InterpState; /// Defines the result of an evaluation. /// -/// The result might be in different forms--one of the pointer types, -/// an APValue, or nothing. +/// The Kind defined if the evaluation was invalid, valid (but empty, e.g. for +/// void expressions) or if we have a valid evaluation result. /// /// We use this class to inspect and diagnose the result, as well as /// convert it to the requested form. @@ -33,8 +32,6 @@ class EvaluationResult final { public: enum ResultKind { Empty, // Initial state. - LValue, // Result is an lvalue/pointer. - RValue, // Result is an rvalue. Invalid, // Result is invalid. Valid, // Result is valid and empty. }; @@ -42,29 +39,18 @@ public: using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>; private: +#ifndef NDEBUG const Context *Ctx = nullptr; - std::variant<std::monostate, Pointer, FunctionPointer, APValue> Value; +#endif + APValue Value; ResultKind Kind = Empty; - DeclTy Source = nullptr; // Currently only needed for dump(). - - EvaluationResult(ResultKind Kind) : Kind(Kind) { - // Leave everything empty. Can be used as an - // error marker or for void return values. - assert(Kind == Valid || Kind == Invalid); - } + DeclTy Source = nullptr; void setSource(DeclTy D) { Source = D; } - void setValue(const APValue &V) { - // V could still be an LValue. + void takeValue(APValue &&V) { assert(empty()); Value = std::move(V); - Kind = RValue; - } - void setFunctionPointer(const FunctionPointer &P) { - assert(empty()); - Value = P; - Kind = LValue; } void setInvalid() { // We are NOT asserting empty() here, since setting it to invalid @@ -77,22 +63,23 @@ private: } public: +#ifndef NDEBUG EvaluationResult(const Context *Ctx) : Ctx(Ctx) {} +#else + EvaluationResult(const Context *Ctx) {} +#endif bool empty() const { return Kind == Empty; } bool isInvalid() const { return Kind == Invalid; } - bool isLValue() const { return Kind == LValue; } - bool isRValue() const { return Kind == RValue; } - bool isPointer() const { return std::holds_alternative<Pointer>(Value); } - /// Returns an APValue for the evaluation result. The returned - /// APValue might be an LValue or RValue. - APValue toAPValue() const; + /// Returns an APValue for the evaluation result. + APValue toAPValue() const { + assert(!empty()); + assert(!isInvalid()); + return Value; + } - /// If the result is an LValue, convert that to an RValue - /// and return it. This may fail, e.g. if the result is an - /// LValue and we can't read from it. - std::optional<APValue> toRValue() const; + APValue stealAPValue() { return std::move(Value); } /// Check that all subobjects of the given pointer have been initialized. bool checkFullyInitialized(InterpState &S, const Pointer &Ptr) const; @@ -105,7 +92,7 @@ public: if (const auto *D = dyn_cast_if_present<ValueDecl>(Source.dyn_cast<const Decl *>())) return D->getType(); - else if (const auto *E = Source.dyn_cast<const Expr *>()) + if (const auto *E = Source.dyn_cast<const Expr *>()) return E->getType(); return QualType(); } diff --git a/clang/lib/AST/ByteCode/Integral.h b/clang/lib/AST/ByteCode/Integral.h index af5cd2d13ecc..131802439f0c 100644 --- a/clang/lib/AST/ByteCode/Integral.h +++ b/clang/lib/AST/ByteCode/Integral.h @@ -318,6 +318,11 @@ private: template <typename T> static bool CheckMulUB(T A, T B, T &R) { if constexpr (std::is_signed_v<T>) { return llvm::MulOverflow<T>(A, B, R); + } else if constexpr (sizeof(T) < sizeof(int)) { + // Silly integer promotion rules will convert both A and B to int, + // even it T is unsigned. Prevent that by manually casting to uint first. + R = static_cast<T>(static_cast<unsigned>(A) * static_cast<unsigned>(B)); + return false; } else { R = A * B; return false; diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 796c24e9071e..b64ed8c2fb49 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -792,6 +792,7 @@ bool CheckLocalLoad(InterpState &S, CodePtr OpPC, const Block *B) { bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK) { + // Block pointers are the only ones we can actually read from. if (!Ptr.isBlockPointer()) { if (Ptr.isZero()) { const auto &Src = S.Current->getSource(OpPC); @@ -804,7 +805,6 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return false; } - // Block pointers are the only ones we can actually read from. if (!Ptr.block()->isAccessible()) { if (!CheckLive(S, OpPC, Ptr, AK)) return false; @@ -812,8 +812,7 @@ bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return false; if (!CheckDummy(S, OpPC, Ptr.block(), AK)) return false; - if (!CheckWeak(S, OpPC, Ptr.block())) - return false; + return CheckWeak(S, OpPC, Ptr.block()); } if (!CheckConstant(S, OpPC, Ptr)) @@ -870,7 +869,7 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { } bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { - if (!Ptr.isBlockPointer()) + if (!Ptr.isBlockPointer() || Ptr.isZero()) return false; if (!Ptr.block()->isAccessible()) { @@ -1207,17 +1206,15 @@ static bool runRecordDestructor(InterpState &S, CodePtr OpPC, } // Destructor of this record. - if (const CXXDestructorDecl *Dtor = R->getDestructor(); - Dtor && !Dtor->isTrivial()) { - const Function *DtorFunc = S.getContext().getOrCreateFunction(Dtor); - if (!DtorFunc) - return false; + const CXXDestructorDecl *Dtor = R->getDestructor(); + assert(Dtor); + assert(!Dtor->isTrivial()); + const Function *DtorFunc = S.getContext().getOrCreateFunction(Dtor); + if (!DtorFunc) + return false; - S.Stk.push<Pointer>(BasePtr); - if (!Call(S, OpPC, DtorFunc, 0)) - return false; - } - return true; + S.Stk.push<Pointer>(BasePtr); + return Call(S, OpPC, DtorFunc, 0); } static bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) { @@ -1229,6 +1226,9 @@ static bool RunDestructors(InterpState &S, CodePtr OpPC, const Block *B) { assert(Desc->isRecord() || Desc->isCompositeArray()); + if (Desc->hasTrivialDtor()) + return true; + if (Desc->isCompositeArray()) { unsigned N = Desc->getNumElems(); if (N == 0) @@ -2117,8 +2117,8 @@ bool DiagTypeid(InterpState &S, CodePtr OpPC) { bool arePotentiallyOverlappingStringLiterals(const Pointer &LHS, const Pointer &RHS) { - unsigned LHSOffset = LHS.getIndex(); - unsigned RHSOffset = RHS.getIndex(); + unsigned LHSOffset = LHS.isOnePastEnd() ? LHS.getNumElems() : LHS.getIndex(); + unsigned RHSOffset = RHS.isOnePastEnd() ? RHS.getNumElems() : RHS.getIndex(); unsigned LHSLength = (LHS.getNumElems() - 1) * LHS.elemSize(); unsigned RHSLength = (RHS.getNumElems() - 1) * RHS.elemSize(); diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 92e60b6b88e6..2da220237803 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -3490,7 +3490,15 @@ inline bool AllocN(InterpState &S, CodePtr OpPC, PrimType T, const Expr *Source, S.Stk.push<Pointer>(0, nullptr); return true; } - assert(NumElements.isPositive()); + if (NumElements.isNegative()) { + if (!IsNoThrow) { + S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_new_negative) + << NumElements.toDiagnosticString(S.getASTContext()); + return false; + } + S.Stk.push<Pointer>(0, nullptr); + return true; + } if (!CheckArraySize(S, OpPC, static_cast<uint64_t>(NumElements))) return false; diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 2e28814abfdd..d418e0ac5d09 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -141,6 +141,22 @@ static void diagnoseNonConstexprBuiltin(InterpState &S, CodePtr OpPC, S.CCEDiag(Loc, diag::note_invalid_subexpr_in_const_expr); } +static llvm::APSInt convertBoolVectorToInt(const Pointer &Val) { + assert(Val.getFieldDesc()->isPrimitiveArray() && + Val.getFieldDesc()->getElemQualType()->isBooleanType() && + "Not a boolean vector"); + unsigned NumElems = Val.getNumElems(); + + // Each element is one bit, so create an integer with NumElts bits. + llvm::APSInt Result(NumElems, 0); + for (unsigned I = 0; I != NumElems; ++I) { + if (Val.elem<bool>(I)) + Result.setBit(I); + } + + return Result; +} + static bool interp__builtin_is_constant_evaluated(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const CallExpr *Call) { @@ -643,8 +659,14 @@ static bool interp__builtin_abs(InterpState &S, CodePtr OpPC, static bool interp__builtin_popcount(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const CallExpr *Call) { - PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType()); - APSInt Val = popToAPSInt(S.Stk, ArgT); + APSInt Val; + if (Call->getArg(0)->getType()->isExtVectorBoolType()) { + const Pointer &Arg = S.Stk.pop<Pointer>(); + Val = convertBoolVectorToInt(Arg); + } else { + PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType()); + Val = popToAPSInt(S.Stk, ArgT); + } pushInteger(S, Val.popcount(), Call->getType()); return true; } @@ -940,8 +962,14 @@ static bool interp__builtin_clz(InterpState &S, CodePtr OpPC, PrimType FallbackT = *S.getContext().classify(Call->getArg(1)); Fallback = popToAPSInt(S.Stk, FallbackT); } - PrimType ValT = *S.getContext().classify(Call->getArg(0)); - const APSInt &Val = popToAPSInt(S.Stk, ValT); + APSInt Val; + if (Call->getArg(0)->getType()->isExtVectorBoolType()) { + const Pointer &Arg = S.Stk.pop<Pointer>(); + Val = convertBoolVectorToInt(Arg); + } else { + PrimType ValT = *S.getContext().classify(Call->getArg(0)); + Val = popToAPSInt(S.Stk, ValT); + } // When the argument is 0, the result of GCC builtins is undefined, whereas // for Microsoft intrinsics, the result is the bit-width of the argument. @@ -971,8 +999,14 @@ static bool interp__builtin_ctz(InterpState &S, CodePtr OpPC, PrimType FallbackT = *S.getContext().classify(Call->getArg(1)); Fallback = popToAPSInt(S.Stk, FallbackT); } - PrimType ValT = *S.getContext().classify(Call->getArg(0)); - const APSInt &Val = popToAPSInt(S.Stk, ValT); + APSInt Val; + if (Call->getArg(0)->getType()->isExtVectorBoolType()) { + const Pointer &Arg = S.Stk.pop<Pointer>(); + Val = convertBoolVectorToInt(Arg); + } else { + PrimType ValT = *S.getContext().classify(Call->getArg(0)); + Val = popToAPSInt(S.Stk, ValT); + } if (Val == 0) { if (Fallback) { @@ -2505,12 +2539,8 @@ static bool interp__builtin_is_within_lifetime(InterpState &S, CodePtr OpPC, } // Check if we're currently running an initializer. - for (InterpFrame *Frame = S.Current; Frame; Frame = Frame->Caller) { - if (const Function *F = Frame->getFunction(); - F && F->isConstructor() && Frame->getThis().block() == Ptr.block()) { - return Error(2); - } - } + if (llvm::is_contained(S.InitializingBlocks, Ptr.block())) + return Error(2); if (S.EvaluatingDecl && Ptr.getDeclDesc()->asVarDecl() == S.EvaluatingDecl) return Error(2); @@ -2518,9 +2548,9 @@ static bool interp__builtin_is_within_lifetime(InterpState &S, CodePtr OpPC, return true; } -static bool interp__builtin_elementwise_sat(InterpState &S, CodePtr OpPC, - const CallExpr *Call, - unsigned BuiltinID) { +static bool interp__builtin_elementwise_int_binop( + InterpState &S, CodePtr OpPC, const CallExpr *Call, + llvm::function_ref<APInt(const APSInt &, const APSInt &)> Fn) { assert(Call->getNumArgs() == 2); // Single integer case. @@ -2530,23 +2560,39 @@ static bool interp__builtin_elementwise_sat(InterpState &S, CodePtr OpPC, S.Stk, *S.getContext().classify(Call->getArg(1)->getType())); APSInt LHS = popToAPSInt( S.Stk, *S.getContext().classify(Call->getArg(0)->getType())); - APInt Result; - if (BuiltinID == Builtin::BI__builtin_elementwise_add_sat) { - Result = LHS.isSigned() ? LHS.sadd_sat(RHS) : LHS.uadd_sat(RHS); - } else if (BuiltinID == Builtin::BI__builtin_elementwise_sub_sat) { - Result = LHS.isSigned() ? LHS.ssub_sat(RHS) : LHS.usub_sat(RHS); - } else { - llvm_unreachable("Wrong builtin ID"); - } - + APInt Result = Fn(LHS, RHS); pushInteger(S, APSInt(std::move(Result), !LHS.isSigned()), Call->getType()); return true; } + const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); + assert(VT->getElementType()->isIntegralOrEnumerationType()); + PrimType ElemT = *S.getContext().classify(VT->getElementType()); + unsigned NumElems = VT->getNumElements(); + bool DestUnsigned = Call->getType()->isUnsignedIntegerOrEnumerationType(); + + // Vector + Scalar case. + if (!Call->getArg(1)->getType()->isVectorType()) { + assert(Call->getArg(1)->getType()->isIntegralOrEnumerationType()); + + APSInt RHS = popToAPSInt( + S.Stk, *S.getContext().classify(Call->getArg(1)->getType())); + const Pointer &LHS = S.Stk.pop<Pointer>(); + const Pointer &Dst = S.Stk.peek<Pointer>(); + + for (unsigned I = 0; I != NumElems; ++I) { + INT_TYPE_SWITCH_NO_BOOL(ElemT, { + Dst.elem<T>(I) = static_cast<T>( + APSInt(Fn(LHS.elem<T>(I).toAPSInt(), RHS), DestUnsigned)); + }); + } + Dst.initializeAllElements(); + return true; + } + // Vector case. assert(Call->getArg(0)->getType()->isVectorType() && Call->getArg(1)->getType()->isVectorType()); - const auto *VT = Call->getArg(0)->getType()->castAs<VectorType>(); assert(VT->getElementType() == Call->getArg(1)->getType()->castAs<VectorType>()->getElementType()); assert(VT->getNumElements() == @@ -2556,46 +2602,12 @@ static bool interp__builtin_elementwise_sat(InterpState &S, CodePtr OpPC, const Pointer &RHS = S.Stk.pop<Pointer>(); const Pointer &LHS = S.Stk.pop<Pointer>(); const Pointer &Dst = S.Stk.peek<Pointer>(); - PrimType ElemT = *S.getContext().classify(VT->getElementType()); - unsigned NumElems = VT->getNumElements(); for (unsigned I = 0; I != NumElems; ++I) { - APSInt Elem1; - APSInt Elem2; INT_TYPE_SWITCH_NO_BOOL(ElemT, { - Elem1 = LHS.elem<T>(I).toAPSInt(); - Elem2 = RHS.elem<T>(I).toAPSInt(); + APSInt Elem1 = LHS.elem<T>(I).toAPSInt(); + APSInt Elem2 = RHS.elem<T>(I).toAPSInt(); + Dst.elem<T>(I) = static_cast<T>(APSInt(Fn(Elem1, Elem2), DestUnsigned)); }); - - APSInt Result; - switch (BuiltinID) { - case Builtin::BI__builtin_elementwise_add_sat: - Result = APSInt(Elem1.isSigned() ? Elem1.sadd_sat(Elem2) - : Elem1.uadd_sat(Elem2), - Call->getType()->isUnsignedIntegerOrEnumerationType()); - break; - case Builtin::BI__builtin_elementwise_sub_sat: - Result = APSInt(Elem1.isSigned() ? Elem1.ssub_sat(Elem2) - : Elem1.usub_sat(Elem2), - Call->getType()->isUnsignedIntegerOrEnumerationType()); - break; - case clang::X86::BI__builtin_ia32_pmulhuw128: - case clang::X86::BI__builtin_ia32_pmulhuw256: - case clang::X86::BI__builtin_ia32_pmulhuw512: - Result = APSInt(llvm::APIntOps::mulhu(Elem1, Elem2), - /*isUnsigned=*/true); - break; - case clang::X86::BI__builtin_ia32_pmulhw128: - case clang::X86::BI__builtin_ia32_pmulhw256: - case clang::X86::BI__builtin_ia32_pmulhw512: - Result = APSInt(llvm::APIntOps::mulhs(Elem1, Elem2), - /*isUnsigned=*/false); - break; - default: - llvm_unreachable("Wrong builtin ID"); - } - - INT_TYPE_SWITCH_NO_BOOL(ElemT, - { Dst.elem<T>(I) = static_cast<T>(Result); }); } Dst.initializeAllElements(); @@ -2724,8 +2736,11 @@ static bool interp__builtin_ia32_pmul(InterpState &S, CodePtr OpPC, return true; } -static bool interp__builtin_elementwise_fma(InterpState &S, CodePtr OpPC, - const CallExpr *Call) { +static bool interp__builtin_elementwise_triop_fp( + InterpState &S, CodePtr OpPC, const CallExpr *Call, + llvm::function_ref<APFloat(const APFloat &, const APFloat &, + const APFloat &, llvm::RoundingMode)> + Fn) { assert(Call->getNumArgs() == 3); FPOptions FPO = Call->getFPFeaturesInEffect(S.Ctx.getLangOpts()); @@ -2744,8 +2759,7 @@ static bool interp__builtin_elementwise_fma(InterpState &S, CodePtr OpPC, const Floating &Z = S.Stk.pop<Floating>(); const Floating &Y = S.Stk.pop<Floating>(); const Floating &X = S.Stk.pop<Floating>(); - APFloat F = X.getAPFloat(); - F.fusedMultiplyAdd(Y.getAPFloat(), Z.getAPFloat(), RM); + APFloat F = Fn(X.getAPFloat(), Y.getAPFloat(), Z.getAPFloat(), RM); Floating Result = S.allocFloat(X.getSemantics()); Result.copy(F); S.Stk.push<Floating>(Result); @@ -2776,8 +2790,8 @@ static bool interp__builtin_elementwise_fma(InterpState &S, CodePtr OpPC, APFloat X = VX.elem<T>(I).getAPFloat(); APFloat Y = VY.elem<T>(I).getAPFloat(); APFloat Z = VZ.elem<T>(I).getAPFloat(); - (void)X.fusedMultiplyAdd(Y, Z, RM); - Dst.elem<Floating>(I) = Floating(X); + APFloat F = Fn(X, Y, Z, RM); + Dst.elem<Floating>(I) = Floating(F); } Dst.initializeAllElements(); return true; @@ -2817,6 +2831,72 @@ static bool interp__builtin_select(InterpState &S, CodePtr OpPC, return true; } +static bool interp__builtin_elementwise_triop( + InterpState &S, CodePtr OpPC, const CallExpr *Call, + llvm::function_ref<APInt(const APSInt &, const APSInt &, const APSInt &)> + Fn) { + assert(Call->getNumArgs() == 3); + + QualType Arg0Type = Call->getArg(0)->getType(); + QualType Arg1Type = Call->getArg(1)->getType(); + QualType Arg2Type = Call->getArg(2)->getType(); + + // Non-vector integer types. + if (!Arg0Type->isVectorType()) { + const APSInt &Op2 = popToAPSInt(S.Stk, *S.getContext().classify(Arg2Type)); + const APSInt &Op1 = popToAPSInt(S.Stk, *S.getContext().classify(Arg1Type)); + const APSInt &Op0 = popToAPSInt(S.Stk, *S.getContext().classify(Arg0Type)); + APSInt Result = APSInt(Fn(Op0, Op1, Op2), Op0.isUnsigned()); + pushInteger(S, Result, Call->getType()); + return true; + } + + const auto *VecT = Arg0Type->castAs<VectorType>(); + const PrimType &ElemT = *S.getContext().classify(VecT->getElementType()); + unsigned NumElems = VecT->getNumElements(); + bool DestUnsigned = Call->getType()->isUnsignedIntegerOrEnumerationType(); + + // Vector + Vector + Scalar case. + if (!Arg2Type->isVectorType()) { + APSInt Op2 = popToAPSInt( + S.Stk, *S.getContext().classify(Call->getArg(2)->getType())); + + const Pointer &Op1 = S.Stk.pop<Pointer>(); + const Pointer &Op0 = S.Stk.pop<Pointer>(); + const Pointer &Dst = S.Stk.peek<Pointer>(); + for (unsigned I = 0; I != NumElems; ++I) { + INT_TYPE_SWITCH_NO_BOOL(ElemT, { + Dst.elem<T>(I) = static_cast<T>(APSInt( + Fn(Op0.elem<T>(I).toAPSInt(), Op1.elem<T>(I).toAPSInt(), Op2), + DestUnsigned)); + }); + } + Dst.initializeAllElements(); + + return true; + } + + // Vector type. + const Pointer &Op2 = S.Stk.pop<Pointer>(); + const Pointer &Op1 = S.Stk.pop<Pointer>(); + const Pointer &Op0 = S.Stk.pop<Pointer>(); + const Pointer &Dst = S.Stk.peek<Pointer>(); + for (unsigned I = 0; I != NumElems; ++I) { + APSInt Val0, Val1, Val2; + INT_TYPE_SWITCH_NO_BOOL(ElemT, { + Val0 = Op0.elem<T>(I).toAPSInt(); + Val1 = Op1.elem<T>(I).toAPSInt(); + Val2 = Op2.elem<T>(I).toAPSInt(); + }); + APSInt Result = APSInt(Fn(Val0, Val1, Val2), Val0.isUnsigned()); + INT_TYPE_SWITCH_NO_BOOL(ElemT, + { Dst.elem<T>(I) = static_cast<T>(Result); }); + } + Dst.initializeAllElements(); + + return true; +} + bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, uint32_t BuiltinID) { if (!S.getASTContext().BuiltinInfo.isConstantEvaluated(BuiltinID)) @@ -3229,14 +3309,139 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, return interp__builtin_is_within_lifetime(S, OpPC, Call); case Builtin::BI__builtin_elementwise_add_sat: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { + return LHS.isSigned() ? LHS.sadd_sat(RHS) : LHS.uadd_sat(RHS); + }); + case Builtin::BI__builtin_elementwise_sub_sat: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { + return LHS.isSigned() ? LHS.ssub_sat(RHS) : LHS.usub_sat(RHS); + }); + + case clang::X86::BI__builtin_ia32_pavgb128: + case clang::X86::BI__builtin_ia32_pavgw128: + case clang::X86::BI__builtin_ia32_pavgb256: + case clang::X86::BI__builtin_ia32_pavgw256: + case clang::X86::BI__builtin_ia32_pavgb512: + case clang::X86::BI__builtin_ia32_pavgw512: + return interp__builtin_elementwise_int_binop(S, OpPC, Call, + llvm::APIntOps::avgCeilU); + case clang::X86::BI__builtin_ia32_pmulhuw128: case clang::X86::BI__builtin_ia32_pmulhuw256: case clang::X86::BI__builtin_ia32_pmulhuw512: + return interp__builtin_elementwise_int_binop(S, OpPC, Call, + llvm::APIntOps::mulhu); + case clang::X86::BI__builtin_ia32_pmulhw128: case clang::X86::BI__builtin_ia32_pmulhw256: case clang::X86::BI__builtin_ia32_pmulhw512: - return interp__builtin_elementwise_sat(S, OpPC, Call, BuiltinID); + return interp__builtin_elementwise_int_binop(S, OpPC, Call, + llvm::APIntOps::mulhs); + + case clang::X86::BI__builtin_ia32_psllv2di: + case clang::X86::BI__builtin_ia32_psllv4di: + case clang::X86::BI__builtin_ia32_psllv4si: + case clang::X86::BI__builtin_ia32_psllv8di: + case clang::X86::BI__builtin_ia32_psllv8hi: + case clang::X86::BI__builtin_ia32_psllv8si: + case clang::X86::BI__builtin_ia32_psllv16hi: + case clang::X86::BI__builtin_ia32_psllv16si: + case clang::X86::BI__builtin_ia32_psllv32hi: + case clang::X86::BI__builtin_ia32_psllwi128: + case clang::X86::BI__builtin_ia32_psllwi256: + case clang::X86::BI__builtin_ia32_psllwi512: + case clang::X86::BI__builtin_ia32_pslldi128: + case clang::X86::BI__builtin_ia32_pslldi256: + case clang::X86::BI__builtin_ia32_pslldi512: + case clang::X86::BI__builtin_ia32_psllqi128: + case clang::X86::BI__builtin_ia32_psllqi256: + case clang::X86::BI__builtin_ia32_psllqi512: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { + if (RHS.uge(LHS.getBitWidth())) { + return APInt::getZero(LHS.getBitWidth()); + } + return LHS.shl(RHS.getZExtValue()); + }); + + case clang::X86::BI__builtin_ia32_psrav4si: + case clang::X86::BI__builtin_ia32_psrav8di: + case clang::X86::BI__builtin_ia32_psrav8hi: + case clang::X86::BI__builtin_ia32_psrav8si: + case clang::X86::BI__builtin_ia32_psrav16hi: + case clang::X86::BI__builtin_ia32_psrav16si: + case clang::X86::BI__builtin_ia32_psrav32hi: + case clang::X86::BI__builtin_ia32_psravq128: + case clang::X86::BI__builtin_ia32_psravq256: + case clang::X86::BI__builtin_ia32_psrawi128: + case clang::X86::BI__builtin_ia32_psrawi256: + case clang::X86::BI__builtin_ia32_psrawi512: + case clang::X86::BI__builtin_ia32_psradi128: + case clang::X86::BI__builtin_ia32_psradi256: + case clang::X86::BI__builtin_ia32_psradi512: + case clang::X86::BI__builtin_ia32_psraqi128: + case clang::X86::BI__builtin_ia32_psraqi256: + case clang::X86::BI__builtin_ia32_psraqi512: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { + if (RHS.uge(LHS.getBitWidth())) { + return LHS.ashr(LHS.getBitWidth() - 1); + } + return LHS.ashr(RHS.getZExtValue()); + }); + + case clang::X86::BI__builtin_ia32_psrlv2di: + case clang::X86::BI__builtin_ia32_psrlv4di: + case clang::X86::BI__builtin_ia32_psrlv4si: + case clang::X86::BI__builtin_ia32_psrlv8di: + case clang::X86::BI__builtin_ia32_psrlv8hi: + case clang::X86::BI__builtin_ia32_psrlv8si: + case clang::X86::BI__builtin_ia32_psrlv16hi: + case clang::X86::BI__builtin_ia32_psrlv16si: + case clang::X86::BI__builtin_ia32_psrlv32hi: + case clang::X86::BI__builtin_ia32_psrlwi128: + case clang::X86::BI__builtin_ia32_psrlwi256: + case clang::X86::BI__builtin_ia32_psrlwi512: + case clang::X86::BI__builtin_ia32_psrldi128: + case clang::X86::BI__builtin_ia32_psrldi256: + case clang::X86::BI__builtin_ia32_psrldi512: + case clang::X86::BI__builtin_ia32_psrlqi128: + case clang::X86::BI__builtin_ia32_psrlqi256: + case clang::X86::BI__builtin_ia32_psrlqi512: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, [](const APSInt &LHS, const APSInt &RHS) { + if (RHS.uge(LHS.getBitWidth())) { + return APInt::getZero(LHS.getBitWidth()); + } + return LHS.lshr(RHS.getZExtValue()); + }); + + case clang::X86::BI__builtin_ia32_vprotbi: + case clang::X86::BI__builtin_ia32_vprotdi: + case clang::X86::BI__builtin_ia32_vprotqi: + case clang::X86::BI__builtin_ia32_vprotwi: + case clang::X86::BI__builtin_ia32_prold128: + case clang::X86::BI__builtin_ia32_prold256: + case clang::X86::BI__builtin_ia32_prold512: + case clang::X86::BI__builtin_ia32_prolq128: + case clang::X86::BI__builtin_ia32_prolq256: + case clang::X86::BI__builtin_ia32_prolq512: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, + [](const APSInt &LHS, const APSInt &RHS) { return LHS.rotl(RHS); }); + + case clang::X86::BI__builtin_ia32_prord128: + case clang::X86::BI__builtin_ia32_prord256: + case clang::X86::BI__builtin_ia32_prord512: + case clang::X86::BI__builtin_ia32_prorq128: + case clang::X86::BI__builtin_ia32_prorq256: + case clang::X86::BI__builtin_ia32_prorq512: + return interp__builtin_elementwise_int_binop( + S, OpPC, Call, + [](const APSInt &LHS, const APSInt &RHS) { return LHS.rotr(RHS); }); case Builtin::BI__builtin_elementwise_max: case Builtin::BI__builtin_elementwise_min: @@ -3251,7 +3456,61 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, return interp__builtin_ia32_pmul(S, OpPC, Call, BuiltinID); case Builtin::BI__builtin_elementwise_fma: - return interp__builtin_elementwise_fma(S, OpPC, Call); + return interp__builtin_elementwise_triop_fp( + S, OpPC, Call, + [](const APFloat &X, const APFloat &Y, const APFloat &Z, + llvm::RoundingMode RM) { + APFloat F = X; + F.fusedMultiplyAdd(Y, Z, RM); + return F; + }); + + case X86::BI__builtin_ia32_vpshldd128: + case X86::BI__builtin_ia32_vpshldd256: + case X86::BI__builtin_ia32_vpshldd512: + case X86::BI__builtin_ia32_vpshldq128: + case X86::BI__builtin_ia32_vpshldq256: + case X86::BI__builtin_ia32_vpshldq512: + case X86::BI__builtin_ia32_vpshldw128: + case X86::BI__builtin_ia32_vpshldw256: + case X86::BI__builtin_ia32_vpshldw512: + return interp__builtin_elementwise_triop( + S, OpPC, Call, + [](const APSInt &Hi, const APSInt &Lo, const APSInt &Amt) { + return llvm::APIntOps::fshl(Hi, Lo, Amt); + }); + + case X86::BI__builtin_ia32_vpshrdd128: + case X86::BI__builtin_ia32_vpshrdd256: + case X86::BI__builtin_ia32_vpshrdd512: + case X86::BI__builtin_ia32_vpshrdq128: + case X86::BI__builtin_ia32_vpshrdq256: + case X86::BI__builtin_ia32_vpshrdq512: + case X86::BI__builtin_ia32_vpshrdw128: + case X86::BI__builtin_ia32_vpshrdw256: + case X86::BI__builtin_ia32_vpshrdw512: + // NOTE: Reversed Hi/Lo operands. + return interp__builtin_elementwise_triop( + S, OpPC, Call, + [](const APSInt &Lo, const APSInt &Hi, const APSInt &Amt) { + return llvm::APIntOps::fshr(Hi, Lo, Amt); + }); + + case clang::X86::BI__builtin_ia32_blendvpd: + case clang::X86::BI__builtin_ia32_blendvpd256: + case clang::X86::BI__builtin_ia32_blendvps: + case clang::X86::BI__builtin_ia32_blendvps256: + return interp__builtin_elementwise_triop_fp( + S, OpPC, Call, + [](const APFloat &F, const APFloat &T, const APFloat &C, + llvm::RoundingMode) { return C.isNegative() ? T : F; }); + + case clang::X86::BI__builtin_ia32_pblendvb128: + case clang::X86::BI__builtin_ia32_pblendvb256: + return interp__builtin_elementwise_triop( + S, OpPC, Call, [](const APSInt &F, const APSInt &T, const APSInt &C) { + return ((APInt)C).isNegative() ? T : F; + }); case X86::BI__builtin_ia32_selectb_128: case X86::BI__builtin_ia32_selectb_256: @@ -3279,6 +3538,13 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, case X86::BI__builtin_ia32_selectpd_512: return interp__builtin_select(S, OpPC, Call); + case Builtin::BI__builtin_elementwise_fshl: + return interp__builtin_elementwise_triop(S, OpPC, Call, + llvm::APIntOps::fshl); + case Builtin::BI__builtin_elementwise_fshr: + return interp__builtin_elementwise_triop(S, OpPC, Call, + llvm::APIntOps::fshr); + default: S.FFDiag(S.Current->getLocation(OpPC), diag::note_invalid_subexpr_in_const_expr) @@ -3303,11 +3569,8 @@ bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E, switch (Node.getKind()) { case OffsetOfNode::Field: { const FieldDecl *MemberDecl = Node.getField(); - const RecordType *RT = CurrentType->getAs<RecordType>(); - if (!RT) - return false; - const RecordDecl *RD = RT->getOriginalDecl()->getDefinitionOrSelf(); - if (RD->isInvalidDecl()) + const auto *RD = CurrentType->getAsRecordDecl(); + if (!RD || RD->isInvalidDecl()) return false; const ASTRecordLayout &RL = S.getASTContext().getASTRecordLayout(RD); unsigned FieldIndex = MemberDecl->getFieldIndex(); @@ -3336,23 +3599,19 @@ bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E, return false; // Find the layout of the class whose base we are looking into. - const RecordType *RT = CurrentType->getAs<RecordType>(); - if (!RT) - return false; - const RecordDecl *RD = RT->getOriginalDecl()->getDefinitionOrSelf(); - if (RD->isInvalidDecl()) + const auto *RD = CurrentType->getAsCXXRecordDecl(); + if (!RD || RD->isInvalidDecl()) return false; const ASTRecordLayout &RL = S.getASTContext().getASTRecordLayout(RD); // Find the base class itself. CurrentType = BaseSpec->getType(); - const RecordType *BaseRT = CurrentType->getAs<RecordType>(); - if (!BaseRT) + const auto *BaseRD = CurrentType->getAsCXXRecordDecl(); + if (!BaseRD) return false; // Add the offset to the base. - Result += RL.getBaseClassOffset(cast<CXXRecordDecl>( - BaseRT->getOriginalDecl()->getDefinitionOrSelf())); + Result += RL.getBaseClassOffset(BaseRD); break; } case OffsetOfNode::Identifier: diff --git a/clang/lib/AST/ByteCode/InterpFrame.cpp b/clang/lib/AST/ByteCode/InterpFrame.cpp index 18400b10c47b..b9dc2aed2311 100644 --- a/clang/lib/AST/ByteCode/InterpFrame.cpp +++ b/clang/lib/AST/ByteCode/InterpFrame.cpp @@ -195,12 +195,6 @@ void InterpFrame::describe(llvm::raw_ostream &OS) const { OS << ")"; } -Frame *InterpFrame::getCaller() const { - if (Caller->Caller) - return Caller; - return S.getSplitFrame(); -} - SourceRange InterpFrame::getCallRange() const { if (!Caller->Func) { if (SourceRange NullRange = S.getRange(nullptr, {}); NullRange.isValid()) diff --git a/clang/lib/AST/ByteCode/InterpFrame.h b/clang/lib/AST/ByteCode/InterpFrame.h index 4be53911b615..cf4d27d341e9 100644 --- a/clang/lib/AST/ByteCode/InterpFrame.h +++ b/clang/lib/AST/ByteCode/InterpFrame.h @@ -59,7 +59,7 @@ public: void describe(llvm::raw_ostream &OS) const override; /// Returns the parent frame object. - Frame *getCaller() const override; + Frame *getCaller() const override { return Caller; } /// Returns the location of the call to the frame. SourceRange getCallRange() const override; diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp index f89967759ff9..1ec4191d2ba3 100644 --- a/clang/lib/AST/ByteCode/InterpState.cpp +++ b/clang/lib/AST/ByteCode/InterpState.cpp @@ -20,19 +20,29 @@ using namespace clang::interp; InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk, Context &Ctx, SourceMapper *M) : Parent(Parent), M(M), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this), - Current(&BottomFrame) {} + Current(&BottomFrame) { + InConstantContext = Parent.InConstantContext; + CheckingPotentialConstantExpression = + Parent.CheckingPotentialConstantExpression; + CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior; +} InterpState::InterpState(State &Parent, Program &P, InterpStack &Stk, Context &Ctx, const Function *Func) : Parent(Parent), M(nullptr), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this, Func, nullptr, CodePtr(), Func->getArgSize()), - Current(&BottomFrame) {} + Current(&BottomFrame) { + InConstantContext = Parent.InConstantContext; + CheckingPotentialConstantExpression = + Parent.CheckingPotentialConstantExpression; + CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior; +} bool InterpState::inConstantContext() const { if (ConstantContextOverride) return *ConstantContextOverride; - return Parent.InConstantContext; + return InConstantContext; } InterpState::~InterpState() { @@ -59,20 +69,11 @@ InterpState::~InterpState() { void InterpState::cleanup() { // As a last resort, make sure all pointers still pointing to a dead block // don't point to it anymore. - Alloc.cleanup(); -} - -Frame *InterpState::getCurrentFrame() { - if (Current && Current->Caller) - return Current; - return Parent.getCurrentFrame(); + if (Alloc) + Alloc->cleanup(); } -bool InterpState::reportOverflow(const Expr *E, const llvm::APSInt &Value) { - QualType Type = E->getType(); - CCEDiag(E, diag::note_constexpr_overflow) << Value << Type; - return noteUndefinedBehavior(); -} +Frame *InterpState::getCurrentFrame() { return Current; } void InterpState::deallocate(Block *B) { assert(B); @@ -103,10 +104,13 @@ void InterpState::deallocate(Block *B) { } bool InterpState::maybeDiagnoseDanglingAllocations() { - bool NoAllocationsLeft = !Alloc.hasAllocations(); + if (!Alloc) + return true; + + bool NoAllocationsLeft = !Alloc->hasAllocations(); if (!checkingPotentialConstantExpression()) { - for (const auto &[Source, Site] : Alloc.allocation_sites()) { + for (const auto &[Source, Site] : Alloc->allocation_sites()) { assert(!Site.empty()); CCEDiag(Source->getExprLoc(), diag::note_constexpr_memory_leak) diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h index 861e4c38049a..e095908bce98 100644 --- a/clang/lib/AST/ByteCode/InterpState.h +++ b/clang/lib/AST/ByteCode/InterpState.h @@ -57,14 +57,11 @@ public: bool diagnosing() const { return getEvalStatus().Diag != nullptr; } // Stack frame accessors. - Frame *getSplitFrame() { return Parent.getCurrentFrame(); } Frame *getCurrentFrame() override; unsigned getCallStackDepth() override { return Current ? (Current->getDepth() + 1) : 1; } - const Frame *getBottomFrame() const override { - return Parent.getBottomFrame(); - } + const Frame *getBottomFrame() const override { return &BottomFrame; } // Access objects from the walker context. Expr::EvalStatus &getEvalStatus() const override { @@ -73,18 +70,12 @@ public: ASTContext &getASTContext() const override { return Parent.getASTContext(); } // Forward status checks and updates to the walker. - bool checkingForUndefinedBehavior() const override { - return Parent.checkingForUndefinedBehavior(); - } bool keepEvaluatingAfterFailure() const override { return Parent.keepEvaluatingAfterFailure(); } bool keepEvaluatingAfterSideEffect() const override { return Parent.keepEvaluatingAfterSideEffect(); } - bool checkingPotentialConstantExpression() const override { - return Parent.checkingPotentialConstantExpression(); - } bool noteUndefinedBehavior() override { return Parent.noteUndefinedBehavior(); } @@ -99,9 +90,6 @@ public: bool hasPriorDiagnostic() override { return Parent.hasPriorDiagnostic(); } bool noteSideEffect() override { return Parent.noteSideEffect(); } - /// 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); @@ -118,7 +106,13 @@ public: void setEvalLocation(SourceLocation SL) { this->EvalLocation = SL; } - DynamicAllocator &getAllocator() { return Alloc; } + DynamicAllocator &getAllocator() { + if (!Alloc) { + Alloc = std::make_unique<DynamicAllocator>(); + } + + return *Alloc.get(); + } /// Diagnose any dynamic allocations that haven't been freed yet. /// Will return \c false if there were any allocations to diagnose, @@ -164,7 +158,7 @@ private: /// Reference to the offset-source mapping. SourceMapper *M; /// Allocator used for dynamic allocations performed via the program. - DynamicAllocator Alloc; + std::unique_ptr<DynamicAllocator> Alloc; public: /// Reference to the module containing all bytecode. diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index 38856ad256a6..ef75b0ded4f1 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -442,26 +442,42 @@ bool Pointer::isInitialized() const { assert(BS.Pointee && "Cannot check if null pointer was initialized"); const Descriptor *Desc = getFieldDesc(); assert(Desc); - if (Desc->isPrimitiveArray()) { - if (isStatic() && BS.Base == 0) - return true; + if (Desc->isPrimitiveArray()) + return isElementInitialized(getIndex()); - InitMapPtr &IM = getInitMap(); + if (asBlockPointer().Base == 0) + return true; + // Field has its bit in an inline descriptor. + return getInlineDesc()->IsInitialized; +} + +bool Pointer::isElementInitialized(unsigned Index) const { + if (!isBlockPointer()) + return true; + const Descriptor *Desc = getFieldDesc(); + assert(Desc); + + if (isStatic() && BS.Base == 0) + return true; + + if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor)) { + const GlobalInlineDescriptor &GD = + *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData()); + return GD.InitState == GlobalInitState::Initialized; + } + + if (Desc->isPrimitiveArray()) { + InitMapPtr &IM = getInitMap(); if (!IM) return false; if (IM->first) return true; - return IM->second->isElementInitialized(getIndex()); + return IM->second->isElementInitialized(Index); } - - if (asBlockPointer().Base == 0) - return true; - - // Field has its bit in an inline descriptor. - return getInlineDesc()->IsInitialized; + return isInitialized(); } void Pointer::initialize() const { @@ -524,6 +540,23 @@ void Pointer::initializeAllElements() const { } } +bool Pointer::allElementsInitialized() const { + assert(getFieldDesc()->isPrimitiveArray()); + assert(isArrayRoot()); + + if (isStatic() && BS.Base == 0) + return true; + + if (isRoot() && BS.Base == sizeof(GlobalInlineDescriptor)) { + const GlobalInlineDescriptor &GD = + *reinterpret_cast<const GlobalInlineDescriptor *>(block()->rawData()); + return GD.InitState == GlobalInitState::Initialized; + } + + InitMapPtr &IM = getInitMap(); + return IM && IM->first; +} + void Pointer::activate() const { // Field has its bit in an inline descriptor. assert(BS.Base != 0 && "Only composite fields can be activated"); @@ -594,9 +627,6 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { if (A.isTypeidPointer() && B.isTypeidPointer()) return true; - if (A.isIntegralPointer() || B.isIntegralPointer()) - return A.getSource() == B.getSource(); - if (A.StorageKind != B.StorageKind) return false; @@ -700,7 +730,7 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, return true; } - if (const auto *RT = Ty->getAs<RecordType>()) { + if (const auto *RT = Ty->getAsCanonical<RecordType>()) { const auto *Record = Ptr.getRecord(); assert(Record && "Missing record descriptor"); @@ -771,13 +801,13 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, R = APValue(APValue::UninitArray{}, NumElems, NumElems); bool Ok = true; - for (unsigned I = 0; I < NumElems; ++I) { + OptPrimType ElemT = Ctx.classify(ElemTy); + for (unsigned I = 0; I != NumElems; ++I) { APValue &Slot = R.getArrayInitializedElt(I); - const Pointer &EP = Ptr.atIndex(I); - if (OptPrimType T = Ctx.classify(ElemTy)) { - TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue(ASTCtx)); + if (ElemT) { + TYPE_SWITCH(*ElemT, Slot = Ptr.elem<T>(I).toAPValue(ASTCtx)); } else { - Ok &= Composite(ElemTy, EP.narrow(), Slot); + Ok &= Composite(ElemTy, Ptr.atIndex(I).narrow(), Slot); } } return Ok; @@ -785,8 +815,11 @@ std::optional<APValue> Pointer::toRValue(const Context &Ctx, // Complex types. if (const auto *CT = Ty->getAs<ComplexType>()) { - QualType ElemTy = CT->getElementType(); + // Can happen via C casts. + if (!Ptr.getFieldDesc()->isPrimitiveArray()) + return false; + QualType ElemTy = CT->getElementType(); if (ElemTy->isIntegerType()) { OptPrimType ElemT = Ctx.classify(ElemTy); assert(ElemT); diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index 0ce54ab0a17d..49d701c3e27b 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -103,9 +103,7 @@ public: Pointer(uint64_t Address, const Descriptor *Desc, uint64_t Offset = 0) : Offset(Offset), StorageKind(Storage::Int), Int{Desc, Address} {} Pointer(const Function *F, uint64_t Offset = 0) - : Offset(Offset), StorageKind(Storage::Fn), Fn(F) { - Fn = FunctionPointer(F); - } + : Offset(Offset), StorageKind(Storage::Fn), Fn(F) {} Pointer(const Type *TypePtr, const Type *TypeInfoType, uint64_t Offset = 0) : Offset(Offset), StorageKind(Storage::Typeid) { Typeid.TypePtr = TypePtr; @@ -122,17 +120,14 @@ public: if (P.StorageKind != StorageKind) return false; if (isIntegralPointer()) - return P.asIntPointer().Value == asIntPointer().Value && - P.asIntPointer().Desc == asIntPointer().Desc && P.Offset == Offset; + return P.Int.Value == Int.Value && P.Int.Desc == Int.Desc && + P.Offset == Offset; if (isFunctionPointer()) - return P.asFunctionPointer().getFunction() == - asFunctionPointer().getFunction() && - P.Offset == Offset; + return P.Fn.getFunction() == Fn.getFunction() && P.Offset == Offset; assert(isBlockPointer()); - return P.asBlockPointer().Pointee == asBlockPointer().Pointee && - P.asBlockPointer().Base == asBlockPointer().Base && + return P.BS.Pointee == BS.Pointee && P.BS.Base == BS.Base && P.Offset == Offset; } @@ -146,10 +141,10 @@ public: uint64_t getIntegerRepresentation() const { if (isIntegralPointer()) - return asIntPointer().Value + (Offset * elemSize()); + return Int.Value + (Offset * elemSize()); if (isFunctionPointer()) - return asFunctionPointer().getIntegerRepresentation() + Offset; - return reinterpret_cast<uint64_t>(asBlockPointer().Pointee) + Offset; + return Fn.getIntegerRepresentation() + Offset; + return reinterpret_cast<uint64_t>(BS.Pointee) + Offset; } /// Converts the pointer to an APValue that is an rvalue. @@ -159,27 +154,25 @@ public: /// Offsets a pointer inside an array. [[nodiscard]] Pointer atIndex(uint64_t Idx) const { if (isIntegralPointer()) - return Pointer(asIntPointer().Value, asIntPointer().Desc, Idx); + return Pointer(Int.Value, Int.Desc, Idx); if (isFunctionPointer()) - return Pointer(asFunctionPointer().getFunction(), Idx); + return Pointer(Fn.getFunction(), Idx); - if (asBlockPointer().Base == RootPtrMark) - return Pointer(asBlockPointer().Pointee, RootPtrMark, - getDeclDesc()->getSize()); + if (BS.Base == RootPtrMark) + return Pointer(BS.Pointee, RootPtrMark, getDeclDesc()->getSize()); uint64_t Off = Idx * elemSize(); if (getFieldDesc()->ElemDesc) Off += sizeof(InlineDescriptor); else Off += sizeof(InitMapPtr); - return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, - asBlockPointer().Base + Off); + return Pointer(BS.Pointee, BS.Base, BS.Base + Off); } /// Creates a pointer to a field. [[nodiscard]] Pointer atField(unsigned Off) const { assert(isBlockPointer()); unsigned Field = Offset + Off; - return Pointer(asBlockPointer().Pointee, Field, Field); + return Pointer(BS.Pointee, Field, Field); } /// Subtract the given offset from the current Base and Offset @@ -187,7 +180,7 @@ public: [[nodiscard]] Pointer atFieldSub(unsigned Off) const { assert(Offset >= Off); unsigned O = Offset - Off; - return Pointer(asBlockPointer().Pointee, O, O); + return Pointer(BS.Pointee, O, O); } /// Restricts the scope of an array element pointer. @@ -199,15 +192,15 @@ public: if (isZero() || isUnknownSizeArray()) return *this; - unsigned Base = asBlockPointer().Base; + unsigned Base = BS.Base; // Pointer to an array of base types - enter block. if (Base == RootPtrMark) - return Pointer(asBlockPointer().Pointee, sizeof(InlineDescriptor), + return Pointer(BS.Pointee, sizeof(InlineDescriptor), Offset == 0 ? Offset : PastEndMark); // Pointer is one past end - magic offset marks that. if (isOnePastEnd()) - return Pointer(asBlockPointer().Pointee, Base, PastEndMark); + return Pointer(BS.Pointee, Base, PastEndMark); if (Offset != Base) { // If we're pointing to a primitive array element, there's nothing to do. @@ -215,7 +208,7 @@ public: return *this; // Pointer is to a composite array element - enter it. if (Offset != Base) - return Pointer(asBlockPointer().Pointee, Offset, Offset); + return Pointer(BS.Pointee, Offset, Offset); } // Otherwise, we're pointing to a non-array element or @@ -226,7 +219,7 @@ public: /// Expands a pointer to the containing array, undoing narrowing. [[nodiscard]] Pointer expand() const { assert(isBlockPointer()); - Block *Pointee = asBlockPointer().Pointee; + Block *Pointee = BS.Pointee; if (isElementPastEnd()) { // Revert to an outer one-past-end pointer. @@ -235,19 +228,18 @@ public: Adjust = sizeof(InitMapPtr); else Adjust = sizeof(InlineDescriptor); - return Pointer(Pointee, asBlockPointer().Base, - asBlockPointer().Base + getSize() + Adjust); + return Pointer(Pointee, BS.Base, BS.Base + getSize() + Adjust); } // Do not step out of array elements. - if (asBlockPointer().Base != Offset) + if (BS.Base != Offset) return *this; if (isRoot()) - return Pointer(Pointee, asBlockPointer().Base, asBlockPointer().Base); + return Pointer(Pointee, BS.Base, BS.Base); // Step into the containing array, if inside one. - unsigned Next = asBlockPointer().Base - getInlineDesc()->Offset; + unsigned Next = BS.Base - getInlineDesc()->Offset; const Descriptor *Desc = (Next == Pointee->getDescriptor()->getMetadataSize()) ? getDeclDesc() @@ -260,19 +252,19 @@ public: /// Checks if the pointer is null. bool isZero() const { if (isBlockPointer()) - return asBlockPointer().Pointee == nullptr; + return BS.Pointee == nullptr; if (isFunctionPointer()) - return asFunctionPointer().isZero(); + return Fn.isZero(); if (isTypeidPointer()) return false; assert(isIntegralPointer()); - return asIntPointer().Value == 0 && Offset == 0; + return Int.Value == 0 && Offset == 0; } /// Checks if the pointer is live. bool isLive() const { if (!isBlockPointer()) return true; - return asBlockPointer().Pointee && !asBlockPointer().Pointee->isDead(); + return BS.Pointee && !BS.Pointee->isDead(); } /// Checks if the item is a field in an object. bool isField() const { @@ -285,13 +277,13 @@ public: /// Accessor for information about the declaration site. const Descriptor *getDeclDesc() const { if (isIntegralPointer()) - return asIntPointer().Desc; + return Int.Desc; if (isFunctionPointer() || isTypeidPointer()) return nullptr; assert(isBlockPointer()); - assert(asBlockPointer().Pointee); - return asBlockPointer().Pointee->Desc; + assert(BS.Pointee); + return BS.Pointee->Desc; } SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); } @@ -300,37 +292,36 @@ public: if (isBlockPointer()) return getDeclDesc()->getSource(); if (isFunctionPointer()) { - const Function *F = asFunctionPointer().getFunction(); + const Function *F = Fn.getFunction(); return F ? F->getDecl() : DeclTy(); } assert(isIntegralPointer()); - return asIntPointer().Desc ? asIntPointer().Desc->getSource() : DeclTy(); + return Int.Desc ? Int.Desc->getSource() : DeclTy(); } /// Returns a pointer to the object of which this pointer is a field. [[nodiscard]] Pointer getBase() const { - if (asBlockPointer().Base == RootPtrMark) { + if (BS.Base == RootPtrMark) { assert(Offset == PastEndMark && "cannot get base of a block"); - return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0); + return Pointer(BS.Pointee, BS.Base, 0); } - unsigned NewBase = asBlockPointer().Base - getInlineDesc()->Offset; - return Pointer(asBlockPointer().Pointee, NewBase, NewBase); + unsigned NewBase = BS.Base - getInlineDesc()->Offset; + return Pointer(BS.Pointee, NewBase, NewBase); } /// Returns the parent array. [[nodiscard]] Pointer getArray() const { - if (asBlockPointer().Base == RootPtrMark) { + if (BS.Base == RootPtrMark) { assert(Offset != 0 && Offset != PastEndMark && "not an array element"); - return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0); + return Pointer(BS.Pointee, BS.Base, 0); } - assert(Offset != asBlockPointer().Base && "not an array element"); - return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, - asBlockPointer().Base); + assert(Offset != BS.Base && "not an array element"); + return Pointer(BS.Pointee, BS.Base, BS.Base); } /// Accessors for information about the innermost field. const Descriptor *getFieldDesc() const { if (isIntegralPointer()) - return asIntPointer().Desc; + return Int.Desc; if (isRoot()) return getDeclDesc(); @@ -342,9 +333,9 @@ public: if (isTypeidPointer()) return QualType(Typeid.TypeInfoType, 0); if (isFunctionPointer()) - return asFunctionPointer().getFunction()->getDecl()->getType(); + return Fn.getFunction()->getDecl()->getType(); - if (inPrimitiveArray() && Offset != asBlockPointer().Base) { + if (inPrimitiveArray() && Offset != BS.Base) { // Unfortunately, complex and vector types are not array types in clang, // but they are for us. if (const auto *AT = getFieldDesc()->getType()->getAsArrayTypeUnsafe()) @@ -357,19 +348,17 @@ public: return getFieldDesc()->getType(); } - [[nodiscard]] Pointer getDeclPtr() const { - return Pointer(asBlockPointer().Pointee); - } + [[nodiscard]] Pointer getDeclPtr() const { return Pointer(BS.Pointee); } /// Returns the element size of the innermost field. size_t elemSize() const { if (isIntegralPointer()) { - if (!asIntPointer().Desc) + if (!Int.Desc) return 1; - return asIntPointer().Desc->getElemSize(); + return Int.Desc->getElemSize(); } - if (asBlockPointer().Base == RootPtrMark) + if (BS.Base == RootPtrMark) return getDeclDesc()->getSize(); return getFieldDesc()->getElemSize(); } @@ -383,24 +372,22 @@ public: unsigned getOffset() const { assert(Offset != PastEndMark && "invalid offset"); assert(isBlockPointer()); - if (asBlockPointer().Base == RootPtrMark) + if (BS.Base == RootPtrMark) return Offset; unsigned Adjust = 0; - if (Offset != asBlockPointer().Base) { + if (Offset != BS.Base) { if (getFieldDesc()->ElemDesc) Adjust = sizeof(InlineDescriptor); else Adjust = sizeof(InitMapPtr); } - return Offset - asBlockPointer().Base - Adjust; + return Offset - BS.Base - Adjust; } /// Whether this array refers to an array, but not /// to the first element. - bool isArrayRoot() const { - return inArray() && Offset == asBlockPointer().Base; - } + bool isArrayRoot() const { return inArray() && Offset == BS.Base; } /// Checks if the innermost field is an array. bool inArray() const { @@ -409,7 +396,7 @@ public: return false; } bool inUnion() const { - if (isBlockPointer() && asBlockPointer().Base >= sizeof(InlineDescriptor)) + if (isBlockPointer() && BS.Base >= sizeof(InlineDescriptor)) return getInlineDesc()->InUnion; return false; }; @@ -431,7 +418,7 @@ public: if (!isBlockPointer()) return false; - const BlockPointer &BP = asBlockPointer(); + const BlockPointer &BP = BS; if (inArray() && BP.Base != Offset) return true; @@ -446,16 +433,15 @@ public: bool isRoot() const { if (isZero() || !isBlockPointer()) return true; - return (asBlockPointer().Base == - asBlockPointer().Pointee->getDescriptor()->getMetadataSize() || - asBlockPointer().Base == 0); + return (BS.Base == BS.Pointee->getDescriptor()->getMetadataSize() || + BS.Base == 0); } /// If this pointer has an InlineDescriptor we can use to initialize. bool canBeInitialized() const { if (!isBlockPointer()) return false; - return asBlockPointer().Pointee && asBlockPointer().Base > 0; + return BS.Pointee && BS.Base > 0; } [[nodiscard]] const BlockPointer &asBlockPointer() const { @@ -497,29 +483,29 @@ public: /// Checks if the storage is extern. bool isExtern() const { if (isBlockPointer()) - return asBlockPointer().Pointee && asBlockPointer().Pointee->isExtern(); + return BS.Pointee && BS.Pointee->isExtern(); return false; } /// Checks if the storage is static. bool isStatic() const { if (!isBlockPointer()) return true; - assert(asBlockPointer().Pointee); - return asBlockPointer().Pointee->isStatic(); + assert(BS.Pointee); + return BS.Pointee->isStatic(); } /// Checks if the storage is temporary. bool isTemporary() const { if (isBlockPointer()) { - assert(asBlockPointer().Pointee); - return asBlockPointer().Pointee->isTemporary(); + assert(BS.Pointee); + return BS.Pointee->isTemporary(); } return false; } /// Checks if the storage has been dynamically allocated. bool isDynamic() const { if (isBlockPointer()) { - assert(asBlockPointer().Pointee); - return asBlockPointer().Pointee->isDynamic(); + assert(BS.Pointee); + return BS.Pointee->isDynamic(); } return false; } @@ -535,15 +521,13 @@ public: bool isWeak() const { if (isFunctionPointer()) - return asFunctionPointer().isWeak(); + return Fn.isWeak(); if (!isBlockPointer()) return false; assert(isBlockPointer()); - return asBlockPointer().Pointee->isWeak(); + return BS.Pointee->isWeak(); } - /// Checks if an object was initialized. - bool isInitialized() const; /// Checks if the object is active. bool isActive() const { if (!isBlockPointer()) @@ -560,7 +544,7 @@ public: if (!isBlockPointer()) return false; - if (const Block *Pointee = asBlockPointer().Pointee) + if (const Block *Pointee = BS.Pointee) return Pointee->isDummy(); return false; } @@ -587,8 +571,8 @@ public: /// Returns the declaration ID. UnsignedOrNone getDeclID() const { if (isBlockPointer()) { - assert(asBlockPointer().Pointee); - return asBlockPointer().Pointee->getDeclID(); + assert(BS.Pointee); + return BS.Pointee->getDeclID(); } return std::nullopt; } @@ -596,9 +580,9 @@ public: /// Returns the byte offset from the start. uint64_t getByteOffset() const { if (isIntegralPointer()) - return asIntPointer().Value + Offset; + return Int.Value + Offset; if (isTypeidPointer()) - return reinterpret_cast<uintptr_t>(asTypeidPointer().TypePtr) + Offset; + return reinterpret_cast<uintptr_t>(Typeid.TypePtr) + Offset; if (isOnePastEnd()) return PastEndMark; return Offset; @@ -611,13 +595,13 @@ public: return getSize() / elemSize(); } - const Block *block() const { return asBlockPointer().Pointee; } + const Block *block() const { return BS.Pointee; } /// If backed by actual data (i.e. a block pointer), return /// an address to that data. const std::byte *getRawAddress() const { assert(isBlockPointer()); - return asBlockPointer().Pointee->rawData() + Offset; + return BS.Pointee->rawData() + Offset; } /// Returns the index into an array. @@ -629,8 +613,7 @@ public: return 0; // narrow()ed element in a composite array. - if (asBlockPointer().Base > sizeof(InlineDescriptor) && - asBlockPointer().Base == Offset) + if (BS.Base > sizeof(InlineDescriptor) && BS.Base == Offset) return 0; if (auto ElemSize = elemSize()) @@ -643,7 +626,7 @@ public: if (!isBlockPointer()) return false; - if (!asBlockPointer().Pointee) + if (!BS.Pointee) return false; if (isUnknownSizeArray()) @@ -676,16 +659,15 @@ public: template <typename T> T &deref() const { assert(isLive() && "Invalid pointer"); assert(isBlockPointer()); - assert(asBlockPointer().Pointee); + assert(BS.Pointee); assert(isDereferencable()); - assert(Offset + sizeof(T) <= - asBlockPointer().Pointee->getDescriptor()->getAllocSize()); + assert(Offset + sizeof(T) <= BS.Pointee->getDescriptor()->getAllocSize()); if (isArrayRoot()) - return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + - asBlockPointer().Base + sizeof(InitMapPtr)); + return *reinterpret_cast<T *>(BS.Pointee->rawData() + BS.Base + + sizeof(InitMapPtr)); - return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + Offset); + return *reinterpret_cast<T *>(BS.Pointee->rawData() + Offset); } /// Dereferences the element at index \p I. @@ -693,18 +675,17 @@ public: template <typename T> T &elem(unsigned I) const { assert(isLive() && "Invalid pointer"); assert(isBlockPointer()); - assert(asBlockPointer().Pointee); + assert(BS.Pointee); assert(isDereferencable()); assert(getFieldDesc()->isPrimitiveArray()); + assert(I < getFieldDesc()->getNumElems()); unsigned ElemByteOffset = I * getFieldDesc()->getElemSize(); - if (isArrayRoot()) - return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + - asBlockPointer().Base + sizeof(InitMapPtr) + - ElemByteOffset); + unsigned ReadOffset = BS.Base + sizeof(InitMapPtr) + ElemByteOffset; + assert(ReadOffset + sizeof(T) <= + BS.Pointee->getDescriptor()->getAllocSize()); - return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + Offset + - ElemByteOffset); + return *reinterpret_cast<T *>(BS.Pointee->rawData() + ReadOffset); } /// Whether this block can be read from at all. This is only true for @@ -724,6 +705,11 @@ public: /// used in situations where we *know* we have initialized *all* elements /// of a primtive array. void initializeAllElements() const; + /// Checks if an object was initialized. + bool isInitialized() const; + /// Like isInitialized(), but for primitive arrays. + bool isElementInitialized(unsigned Index) const; + bool allElementsInitialized() const; /// Activats a field. void activate() const; /// Deactivates an entire strurcutre. @@ -732,7 +718,7 @@ public: Lifetime getLifetime() const { if (!isBlockPointer()) return Lifetime::Started; - if (asBlockPointer().Base < sizeof(InlineDescriptor)) + if (BS.Base < sizeof(InlineDescriptor)) return Lifetime::Started; return getInlineDesc()->LifeState; } @@ -740,7 +726,7 @@ public: void endLifetime() const { if (!isBlockPointer()) return; - if (asBlockPointer().Base < sizeof(InlineDescriptor)) + if (BS.Base < sizeof(InlineDescriptor)) return; getInlineDesc()->LifeState = Lifetime::Ended; } @@ -748,7 +734,7 @@ public: void startLifetime() const { if (!isBlockPointer()) return; - if (asBlockPointer().Base < sizeof(InlineDescriptor)) + if (BS.Base < sizeof(InlineDescriptor)) return; getInlineDesc()->LifeState = Lifetime::Started; } @@ -801,10 +787,10 @@ private: /// Returns the embedded descriptor preceding a field. InlineDescriptor *getInlineDesc() const { assert(isBlockPointer()); - assert(asBlockPointer().Base != sizeof(GlobalInlineDescriptor)); - assert(asBlockPointer().Base <= asBlockPointer().Pointee->getSize()); - assert(asBlockPointer().Base >= sizeof(InlineDescriptor)); - return getDescriptor(asBlockPointer().Base); + assert(BS.Base != sizeof(GlobalInlineDescriptor)); + assert(BS.Base <= BS.Pointee->getSize()); + assert(BS.Base >= sizeof(InlineDescriptor)); + return getDescriptor(BS.Base); } /// Returns a descriptor at a given offset. @@ -812,8 +798,8 @@ private: assert(Offset != 0 && "Not a nested pointer"); assert(isBlockPointer()); assert(!isZero()); - return reinterpret_cast<InlineDescriptor *>( - asBlockPointer().Pointee->rawData() + Offset) - + return reinterpret_cast<InlineDescriptor *>(BS.Pointee->rawData() + + Offset) - 1; } @@ -821,8 +807,7 @@ private: InitMapPtr &getInitMap() const { assert(isBlockPointer()); assert(!isZero()); - return *reinterpret_cast<InitMapPtr *>(asBlockPointer().Pointee->rawData() + - asBlockPointer().Base); + return *reinterpret_cast<InitMapPtr *>(BS.Pointee->rawData() + BS.Base); } /// Offset into the storage. diff --git a/clang/lib/AST/ByteCode/PrimType.h b/clang/lib/AST/ByteCode/PrimType.h index 6e3a49cadcbf..54fd39ac6fcc 100644 --- a/clang/lib/AST/ByteCode/PrimType.h +++ b/clang/lib/AST/ByteCode/PrimType.h @@ -272,14 +272,4 @@ static inline bool aligned(const void *P) { } \ } while (0) -#define COMPOSITE_TYPE_SWITCH(Expr, B, D) \ - do { \ - switch (Expr) { \ - TYPE_SWITCH_CASE(PT_Ptr, B) \ - default: { \ - D; \ - break; \ - } \ - } \ - } while (0) #endif diff --git a/clang/lib/AST/ByteCode/Program.cpp b/clang/lib/AST/ByteCode/Program.cpp index 5d72044af969..75bfd9fd2d8e 100644 --- a/clang/lib/AST/ByteCode/Program.cpp +++ b/clang/lib/AST/ByteCode/Program.cpp @@ -101,7 +101,7 @@ unsigned Program::createGlobalString(const StringLiteral *S, const Expr *Base) { } } } - Ptr.initialize(); + Ptr.initializeAllElements(); return GlobalIndex; } @@ -111,7 +111,7 @@ Pointer Program::getPtrGlobal(unsigned Idx) const { return Pointer(Globals[Idx]->block()); } -std::optional<unsigned> Program::getGlobal(const ValueDecl *VD) { +UnsignedOrNone Program::getGlobal(const ValueDecl *VD) { if (auto It = GlobalIndices.find(VD); It != GlobalIndices.end()) return It->second; @@ -131,14 +131,14 @@ std::optional<unsigned> Program::getGlobal(const ValueDecl *VD) { return std::nullopt; } -std::optional<unsigned> Program::getGlobal(const Expr *E) { +UnsignedOrNone Program::getGlobal(const Expr *E) { if (auto It = GlobalIndices.find(E); It != GlobalIndices.end()) return It->second; return std::nullopt; } -std::optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD, - const Expr *Init) { +UnsignedOrNone Program::getOrCreateGlobal(const ValueDecl *VD, + const Expr *Init) { if (auto Idx = getGlobal(VD)) return Idx; @@ -195,8 +195,7 @@ unsigned Program::getOrCreateDummy(const DeclTy &D) { return I; } -std::optional<unsigned> Program::createGlobal(const ValueDecl *VD, - const Expr *Init) { +UnsignedOrNone Program::createGlobal(const ValueDecl *VD, const Expr *Init) { bool IsStatic, IsExtern; bool IsWeak = VD->isWeak(); if (const auto *Var = dyn_cast<VarDecl>(VD)) { @@ -213,7 +212,7 @@ std::optional<unsigned> Program::createGlobal(const ValueDecl *VD, // Register all previous declarations as well. For extern blocks, just replace // the index with the new variable. - std::optional<unsigned> Idx = + UnsignedOrNone Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern, IsWeak, Init); if (!Idx) return std::nullopt; @@ -240,7 +239,7 @@ std::optional<unsigned> Program::createGlobal(const ValueDecl *VD, return *Idx; } -std::optional<unsigned> Program::createGlobal(const Expr *E) { +UnsignedOrNone Program::createGlobal(const Expr *E) { if (auto Idx = getGlobal(E)) return Idx; if (auto Idx = createGlobal(E, E->getType(), /*isStatic=*/true, @@ -251,9 +250,9 @@ std::optional<unsigned> Program::createGlobal(const Expr *E) { return std::nullopt; } -std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, - bool IsStatic, bool IsExtern, - bool IsWeak, const Expr *Init) { +UnsignedOrNone Program::createGlobal(const DeclTy &D, QualType Ty, + bool IsStatic, bool IsExtern, bool IsWeak, + const Expr *Init) { // Create a descriptor for the global. Descriptor *Desc; const bool IsConst = Ty.isConstQualified(); @@ -332,10 +331,9 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { continue; // In error cases, the base might not be a RecordType. - const auto *RT = Spec.getType()->getAs<RecordType>(); - if (!RT) + const auto *BD = Spec.getType()->getAsCXXRecordDecl(); + if (!BD) return nullptr; - const RecordDecl *BD = RT->getOriginalDecl()->getDefinitionOrSelf(); const Record *BR = getOrCreateRecord(BD); const Descriptor *Desc = GetBaseDesc(BD, BR); @@ -348,11 +346,7 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { } for (const CXXBaseSpecifier &Spec : CD->vbases()) { - const auto *RT = Spec.getType()->getAs<RecordType>(); - if (!RT) - return nullptr; - - const RecordDecl *BD = RT->getOriginalDecl()->getDefinitionOrSelf(); + const auto *BD = Spec.getType()->castAsCXXRecordDecl(); const Record *BR = getOrCreateRecord(BD); const Descriptor *Desc = GetBaseDesc(BD, BR); @@ -408,9 +402,8 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, const Expr *Init) { // Classes and structures. - if (const auto *RT = Ty->getAs<RecordType>()) { - if (const auto *Record = - getOrCreateRecord(RT->getOriginalDecl()->getDefinitionOrSelf())) + if (const auto *RD = Ty->getAsRecordDecl()) { + if (const auto *Record = getOrCreateRecord(RD)) return allocateDescriptor(D, Record, MDSize, IsConst, IsTemporary, IsMutable, IsVolatile); return allocateDescriptor(D, MDSize); diff --git a/clang/lib/AST/ByteCode/Program.h b/clang/lib/AST/ByteCode/Program.h index 90b48ee5b669..28fcc97f5339 100644 --- a/clang/lib/AST/ByteCode/Program.h +++ b/clang/lib/AST/ByteCode/Program.h @@ -78,21 +78,21 @@ public: } /// Finds a global's index. - std::optional<unsigned> getGlobal(const ValueDecl *VD); - std::optional<unsigned> getGlobal(const Expr *E); + UnsignedOrNone getGlobal(const ValueDecl *VD); + UnsignedOrNone getGlobal(const Expr *E); /// Returns or creates a global an creates an index to it. - std::optional<unsigned> getOrCreateGlobal(const ValueDecl *VD, - const Expr *Init = nullptr); + UnsignedOrNone getOrCreateGlobal(const ValueDecl *VD, + const Expr *Init = nullptr); /// Returns or creates a dummy value for unknown declarations. unsigned getOrCreateDummy(const DeclTy &D); /// Creates a global and returns its index. - std::optional<unsigned> createGlobal(const ValueDecl *VD, const Expr *Init); + UnsignedOrNone createGlobal(const ValueDecl *VD, const Expr *Init); /// Creates a global from a lifetime-extended temporary. - std::optional<unsigned> createGlobal(const Expr *E); + UnsignedOrNone createGlobal(const Expr *E); /// Creates a new function from a code range. template <typename... Ts> @@ -165,9 +165,9 @@ public: private: friend class DeclScope; - std::optional<unsigned> createGlobal(const DeclTy &D, QualType Ty, - bool IsStatic, bool IsExtern, - bool IsWeak, const Expr *Init = nullptr); + UnsignedOrNone createGlobal(const DeclTy &D, QualType Ty, bool IsStatic, + bool IsExtern, bool IsWeak, + const Expr *Init = nullptr); /// Reference to the VM context. Context &Ctx; diff --git a/clang/lib/AST/ByteCode/Record.cpp b/clang/lib/AST/ByteCode/Record.cpp index a7934ccb4e55..ff5c82d24457 100644 --- a/clang/lib/AST/ByteCode/Record.cpp +++ b/clang/lib/AST/ByteCode/Record.cpp @@ -37,6 +37,13 @@ std::string Record::getName() const { return Ret; } +bool Record::hasTrivialDtor() const { + if (isAnonymousUnion()) + return true; + const CXXDestructorDecl *Dtor = getDestructor(); + return !Dtor || Dtor->isTrivial(); +} + const Record::Field *Record::getField(const FieldDecl *FD) const { auto It = FieldMap.find(FD->getFirstDecl()); assert(It != FieldMap.end() && "Missing field"); @@ -50,10 +57,8 @@ const Record::Base *Record::getBase(const RecordDecl *FD) const { } const Record::Base *Record::getBase(QualType T) const { - if (auto *RT = T->getAs<RecordType>()) { - const RecordDecl *RD = RT->getOriginalDecl()->getDefinitionOrSelf(); + if (auto *RD = T->getAsCXXRecordDecl()) return BaseMap.lookup(RD); - } return nullptr; } diff --git a/clang/lib/AST/ByteCode/Record.h b/clang/lib/AST/ByteCode/Record.h index 93ca43046180..8245eeff2f20 100644 --- a/clang/lib/AST/ByteCode/Record.h +++ b/clang/lib/AST/ByteCode/Record.h @@ -76,6 +76,10 @@ public: return nullptr; } + /// Returns true for anonymous unions and records + /// with no destructor or for those with a trivial destructor. + bool hasTrivialDtor() const; + using const_field_iter = FieldList::const_iterator; llvm::iterator_range<const_field_iter> fields() const { return llvm::make_range(Fields.begin(), Fields.end()); diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h index 6fc33222ac95..387ce396a723 100644 --- a/clang/lib/AST/ByteCode/State.h +++ b/clang/lib/AST/ByteCode/State.h @@ -59,8 +59,6 @@ class State { public: virtual ~State(); - virtual bool checkingForUndefinedBehavior() const = 0; - virtual bool checkingPotentialConstantExpression() const = 0; virtual bool noteUndefinedBehavior() = 0; virtual bool keepEvaluatingAfterFailure() const = 0; virtual bool keepEvaluatingAfterSideEffect() const = 0; @@ -75,6 +73,16 @@ public: virtual unsigned getCallStackDepth() = 0; virtual bool noteSideEffect() = 0; + /// Are we checking whether the expression is a potential constant + /// expression? + bool checkingPotentialConstantExpression() const { + return CheckingPotentialConstantExpression; + } + /// Are we checking an expression for overflow? + bool checkingForUndefinedBehavior() const { + return CheckingForUndefinedBehavior; + } + public: State() = default; /// Diagnose that the evaluation could not be folded (FF => FoldFailure) @@ -128,6 +136,19 @@ public: /// constant value. bool InConstantContext = false; + /// Whether we're checking that an expression is a potential constant + /// expression. If so, do not fail on constructs that could become constant + /// later on (such as a use of an undefined global). + bool CheckingPotentialConstantExpression = false; + + /// Whether we're checking for an expression that has undefined behavior. + /// If so, we will produce warnings if we encounter an operation that is + /// always undefined. + /// + /// Note that we still need to evaluate the expression normally when this + /// is set; this is used when evaluating ICEs in C. + bool CheckingForUndefinedBehavior = false; + private: void addCallStack(unsigned Limit); |
