diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/AST/Interp')
37 files changed, 2741 insertions, 811 deletions
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Boolean.h b/contrib/llvm-project/clang/lib/AST/Interp/Boolean.h index 2baa717311bc..3122388a49a5 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Boolean.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/Boolean.h @@ -22,7 +22,7 @@ namespace clang { namespace interp { /// Wrapper around boolean types. -class Boolean { +class Boolean final { private: /// Underlying boolean. bool V; @@ -46,9 +46,15 @@ class Boolean { Boolean operator-() const { return Boolean(V); } Boolean operator~() const { return Boolean(true); } - explicit operator unsigned() const { return V; } + explicit operator int8_t() const { return V; } + explicit operator uint8_t() const { return V; } + explicit operator int16_t() const { return V; } + explicit operator uint16_t() const { return V; } + explicit operator int32_t() const { return V; } + explicit operator uint32_t() const { return V; } explicit operator int64_t() const { return V; } explicit operator uint64_t() const { return V; } + explicit operator bool() const { return V; } APSInt toAPSInt() const { return APSInt(APInt(1, static_cast<uint64_t>(V), false), true); @@ -84,9 +90,10 @@ class Boolean { static Boolean min(unsigned NumBits) { return Boolean(false); } static Boolean max(unsigned NumBits) { return Boolean(true); } - template <typename T> - static std::enable_if_t<std::is_integral<T>::value, Boolean> from(T Value) { - return Boolean(Value != 0); + template <typename T> static Boolean from(T Value) { + if constexpr (std::is_integral<T>::value) + return Boolean(Value != 0); + return Boolean(static_cast<decltype(Boolean::V)>(Value) != 0); } template <unsigned SrcBits, bool SrcSign> @@ -134,6 +141,16 @@ class Boolean { *R = Boolean(A.V && B.V); return false; } + + static bool inv(Boolean A, Boolean *R) { + *R = Boolean(!A.V); + return false; + } + + static bool neg(Boolean A, Boolean *R) { + *R = Boolean(A.V); + return false; + } }; inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Boolean &B) { diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.cpp index a69b23fd613c..4633d1e0823b 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -19,51 +19,72 @@ using namespace clang::interp; using APSInt = llvm::APSInt; using Error = llvm::Error; -Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) { - // Do not try to compile undefined functions. - if (!F->isDefined(F) || (!F->hasBody() && F->willHaveBody())) - return nullptr; - - // Set up argument indices. - unsigned ParamOffset = 0; - SmallVector<PrimType, 8> ParamTypes; - llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors; - - // If the return is not a primitive, a pointer to the storage where the value - // is initialized in is passed as the first argument. - QualType Ty = F->getReturnType(); - if (!Ty->isVoidType() && !Ctx.classify(Ty)) { - ParamTypes.push_back(PT_Ptr); - ParamOffset += align(primSize(PT_Ptr)); - } +Expected<Function *> +ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) { + // Function is not defined at all or not yet. We will + // create a Function instance but not compile the body. That + // will (maybe) happen later. + bool HasBody = FuncDecl->hasBody(FuncDecl); + + // Create a handle over the emitted code. + Function *Func = P.getFunction(FuncDecl); + if (!Func) { + // Set up argument indices. + unsigned ParamOffset = 0; + SmallVector<PrimType, 8> ParamTypes; + llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors; + + // If the return is not a primitive, a pointer to the storage where the + // value is initialized in is passed as the first argument. See 'RVO' + // elsewhere in the code. + QualType Ty = FuncDecl->getReturnType(); + bool HasRVO = false; + if (!Ty->isVoidType() && !Ctx.classify(Ty)) { + HasRVO = true; + ParamTypes.push_back(PT_Ptr); + ParamOffset += align(primSize(PT_Ptr)); + } + + // If the function decl is a member decl, the next parameter is + // the 'this' pointer. This parameter is pop()ed from the + // InterpStack when calling the function. + bool HasThisPointer = false; + if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl); + MD && MD->isInstance()) { + HasThisPointer = true; + ParamTypes.push_back(PT_Ptr); + ParamOffset += align(primSize(PT_Ptr)); + } - // Assign descriptors to all parameters. - // Composite objects are lowered to pointers. - for (const ParmVarDecl *PD : F->parameters()) { - PrimType Ty; - if (llvm::Optional<PrimType> T = Ctx.classify(PD->getType())) { - Ty = *T; - } else { - Ty = PT_Ptr; + // Assign descriptors to all parameters. + // Composite objects are lowered to pointers. + for (const ParmVarDecl *PD : FuncDecl->parameters()) { + PrimType Ty = Ctx.classify(PD->getType()).value_or(PT_Ptr); + Descriptor *Desc = P.createDescriptor(PD, Ty); + ParamDescriptors.insert({ParamOffset, {Ty, Desc}}); + Params.insert({PD, ParamOffset}); + ParamOffset += align(primSize(Ty)); + ParamTypes.push_back(Ty); } - Descriptor *Desc = P.createDescriptor(PD, Ty); - ParamDescriptors.insert({ParamOffset, {Ty, Desc}}); - Params.insert({PD, ParamOffset}); - ParamOffset += align(primSize(Ty)); - ParamTypes.push_back(Ty); + Func = + P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes), + std::move(ParamDescriptors), HasThisPointer, HasRVO); } - // Create a handle over the emitted code. - Function *Func = P.createFunction(F, ParamOffset, std::move(ParamTypes), - std::move(ParamDescriptors)); + assert(Func); + if (!HasBody) + return Func; + // Compile the function body. - if (!F->isConstexpr() || !visitFunc(F)) { + if (!FuncDecl->isConstexpr() || !visitFunc(FuncDecl)) { // Return a dummy function if compilation failed. if (BailLocation) return llvm::make_error<ByteCodeGenError>(*BailLocation); - else + else { + Func->setIsFullyCompiled(true); return Func; + } } else { // Create scopes from descriptors. llvm::SmallVector<Scope, 2> Scopes; @@ -74,6 +95,7 @@ Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) { // Set the function's code. Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap), std::move(Scopes)); + Func->setIsFullyCompiled(true); return Func; } } @@ -94,7 +116,8 @@ void ByteCodeEmitter::emitLabel(LabelTy Label) { using namespace llvm::support; /// Rewrite the operand of all jumps to this label. - void *Location = Code.data() + Reloc - sizeof(int32_t); + void *Location = Code.data() + Reloc - align(sizeof(int32_t)); + assert(aligned(Location)); const int32_t Offset = Target - static_cast<int64_t>(Reloc); endian::write<int32_t, endianness::native, 1>(Location, Offset); } @@ -104,7 +127,9 @@ void ByteCodeEmitter::emitLabel(LabelTy Label) { int32_t ByteCodeEmitter::getOffset(LabelTy Label) { // Compute the PC offset which the jump is relative to. - const int64_t Position = Code.size() + sizeof(Opcode) + sizeof(int32_t); + const int64_t Position = + Code.size() + align(sizeof(Opcode)) + align(sizeof(int32_t)); + assert(aligned(Position)); // If target is known, compute jump offset. auto It = LabelOffsets.find(Label); @@ -126,30 +151,32 @@ bool ByteCodeEmitter::bail(const SourceLocation &Loc) { /// Helper to write bytecode and bail out if 32-bit offsets become invalid. /// Pointers will be automatically marshalled as 32-bit IDs. template <typename T> -static std::enable_if_t<!std::is_pointer<T>::value, void> -emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) { - size_t Size = sizeof(Val); - if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { - Success = false; - return; - } +static void emit(Program &P, std::vector<char> &Code, const T &Val, + bool &Success) { + size_t Size; - const char *Data = reinterpret_cast<const char *>(&Val); - Code.insert(Code.end(), Data, Data + Size); -} + if constexpr (std::is_pointer_v<T>) + Size = sizeof(uint32_t); + else + Size = sizeof(T); -template <typename T> -static std::enable_if_t<std::is_pointer<T>::value, void> -emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) { - size_t Size = sizeof(uint32_t); if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { Success = false; return; } - uint32_t ID = P.getOrCreateNativePointer(Val); - const char *Data = reinterpret_cast<const char *>(&ID); - Code.insert(Code.end(), Data, Data + Size); + // Access must be aligned! + size_t ValPos = align(Code.size()); + Size = align(Size); + assert(aligned(ValPos + Size)); + Code.resize(ValPos + Size); + + if constexpr (!std::is_pointer_v<T>) { + new (Code.data() + ValPos) T(Val); + } else { + uint32_t ID = P.getOrCreateNativePointer(Val); + new (Code.data() + ValPos) uint32_t(ID); + } } template <typename... Tys> diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.h b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.h index 03452a350c96..30da06b20250 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.h @@ -37,7 +37,7 @@ protected: public: /// Compiles the function into the module. - llvm::Expected<Function *> compileFunc(const FunctionDecl *F); + llvm::Expected<Function *> compileFunc(const FunctionDecl *FuncDecl); protected: ByteCodeEmitter(Context &Ctx, Program &P) : Ctx(Ctx), P(P) {} @@ -83,7 +83,7 @@ private: /// Offset of the next local variable. unsigned NextLocalOffset = 0; /// Location of a failure. - llvm::Optional<SourceLocation> BailLocation; + std::optional<SourceLocation> BailLocation; /// Label information for linker. llvm::DenseMap<LabelTy, unsigned> LabelOffsets; /// Location of label relocations. diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 9b729e347a24..615dbdefefbe 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -9,6 +9,7 @@ #include "ByteCodeExprGen.h" #include "ByteCodeEmitter.h" #include "ByteCodeGenError.h" +#include "ByteCodeStmtGen.h" #include "Context.h" #include "Function.h" #include "PrimType.h" @@ -19,8 +20,6 @@ using namespace clang; using namespace clang::interp; using APSInt = llvm::APSInt; -template <typename T> using Expected = llvm::Expected<T>; -template <typename T> using Optional = llvm::Optional<T>; namespace clang { namespace interp { @@ -42,45 +41,19 @@ private: /// Scope used to handle initialization methods. template <class Emitter> class OptionScope { public: - using InitFnRef = typename ByteCodeExprGen<Emitter>::InitFnRef; - using ChainedInitFnRef = std::function<bool(InitFnRef)>; - /// Root constructor, compiling or discarding primitives. OptionScope(ByteCodeExprGen<Emitter> *Ctx, bool NewDiscardResult) - : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), - OldInitFn(std::move(Ctx->InitFn)) { + : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult) { Ctx->DiscardResult = NewDiscardResult; - Ctx->InitFn = llvm::Optional<InitFnRef>{}; - } - - /// Root constructor, setting up compilation state. - OptionScope(ByteCodeExprGen<Emitter> *Ctx, InitFnRef NewInitFn) - : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), - OldInitFn(std::move(Ctx->InitFn)) { - Ctx->DiscardResult = true; - Ctx->InitFn = NewInitFn; } - /// Extends the chain of initialisation pointers. - OptionScope(ByteCodeExprGen<Emitter> *Ctx, ChainedInitFnRef NewInitFn) - : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), - OldInitFn(std::move(Ctx->InitFn)) { - assert(OldInitFn && "missing initializer"); - Ctx->InitFn = [this, NewInitFn] { return NewInitFn(*OldInitFn); }; - } - - ~OptionScope() { - Ctx->DiscardResult = OldDiscardResult; - Ctx->InitFn = std::move(OldInitFn); - } + ~OptionScope() { Ctx->DiscardResult = OldDiscardResult; } private: /// Parent context. ByteCodeExprGen<Emitter> *Ctx; /// Old discard flag to restore. bool OldDiscardResult; - /// Old pointer emitter to restore. - llvm::Optional<InitFnRef> OldInitFn; }; } // namespace interp @@ -106,6 +79,22 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) { }); } + case CK_UncheckedDerivedToBase: + case CK_DerivedToBase: { + if (!this->visit(SubExpr)) + return false; + + const CXXRecordDecl *FromDecl = getRecordDecl(SubExpr); + assert(FromDecl); + const CXXRecordDecl *ToDecl = getRecordDecl(CE); + assert(ToDecl); + const Record *R = getRecord(FromDecl); + const Record::Base *ToBase = R->getBase(ToDecl); + assert(ToBase); + + return this->emitGetPtrBase(ToBase->Offset, CE); + } + case CK_ArrayToPointerDecay: case CK_AtomicToNonAtomic: case CK_ConstructorConversion: @@ -113,16 +102,30 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) { case CK_NonAtomicToAtomic: case CK_NoOp: case CK_UserDefinedConversion: - return this->Visit(SubExpr); + case CK_NullToPointer: + return this->visit(SubExpr); + + case CK_IntegralToBoolean: + case CK_IntegralCast: { + std::optional<PrimType> FromT = classify(SubExpr->getType()); + std::optional<PrimType> ToT = classify(CE->getType()); + if (!FromT || !ToT) + return false; + + if (!this->visit(SubExpr)) + return false; + + // TODO: Emit this only if FromT != ToT. + return this->emitCast(*FromT, *ToT, CE); + } case CK_ToVoid: return discard(SubExpr); - default: { - // TODO: implement other casts. - return this->bail(CE); - } + default: + assert(false && "Cast not implemented"); } + llvm_unreachable("Unhandled clang::CastKind enum"); } template <class Emitter> @@ -130,16 +133,12 @@ bool ByteCodeExprGen<Emitter>::VisitIntegerLiteral(const IntegerLiteral *LE) { if (DiscardResult) return true; - auto Val = LE->getValue(); - QualType LitTy = LE->getType(); - if (Optional<PrimType> T = classify(LitTy)) - return emitConst(*T, getIntWidth(LitTy), LE->getValue(), LE); - return this->bail(LE); + return this->emitConst(LE->getValue(), LE); } template <class Emitter> bool ByteCodeExprGen<Emitter>::VisitParenExpr(const ParenExpr *PE) { - return this->Visit(PE->getSubExpr()); + return this->visit(PE->getSubExpr()); } template <class Emitter> @@ -152,7 +151,7 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) { case BO_Comma: if (!discard(LHS)) return false; - if (!this->Visit(RHS)) + if (!this->visit(RHS)) return false; return true; default: @@ -160,53 +159,398 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) { } // Typecheck the args. - Optional<PrimType> LT = classify(LHS->getType()); - Optional<PrimType> RT = classify(RHS->getType()); - if (!LT || !RT) { + std::optional<PrimType> LT = classify(LHS->getType()); + std::optional<PrimType> RT = classify(RHS->getType()); + std::optional<PrimType> T = classify(BO->getType()); + if (!LT || !RT || !T) { return this->bail(BO); } - if (Optional<PrimType> T = classify(BO->getType())) { - if (!visit(LHS)) + auto Discard = [this, T, BO](bool Result) { + if (!Result) return false; - if (!visit(RHS)) + return DiscardResult ? this->emitPop(*T, BO) : true; + }; + + // Pointer arithmetic special case. + if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub) { + if (*T == PT_Ptr || (*LT == PT_Ptr && *RT == PT_Ptr)) + return this->VisitPointerArithBinOp(BO); + } + + if (!visit(LHS) || !visit(RHS)) + return false; + + switch (BO->getOpcode()) { + case BO_EQ: + return Discard(this->emitEQ(*LT, BO)); + case BO_NE: + return Discard(this->emitNE(*LT, BO)); + case BO_LT: + return Discard(this->emitLT(*LT, BO)); + case BO_LE: + return Discard(this->emitLE(*LT, BO)); + case BO_GT: + return Discard(this->emitGT(*LT, BO)); + case BO_GE: + return Discard(this->emitGE(*LT, BO)); + case BO_Sub: + return Discard(this->emitSub(*T, BO)); + case BO_Add: + return Discard(this->emitAdd(*T, BO)); + case BO_Mul: + return Discard(this->emitMul(*T, BO)); + case BO_Rem: + return Discard(this->emitRem(*T, BO)); + case BO_Div: + return Discard(this->emitDiv(*T, BO)); + case BO_Assign: + if (DiscardResult) + return this->emitStorePop(*T, BO); + return this->emitStore(*T, BO); + case BO_And: + return Discard(this->emitBitAnd(*T, BO)); + case BO_Or: + return Discard(this->emitBitOr(*T, BO)); + case BO_Shl: + return Discard(this->emitShl(*LT, *RT, BO)); + case BO_Shr: + return Discard(this->emitShr(*LT, *RT, BO)); + case BO_Xor: + return Discard(this->emitBitXor(*T, BO)); + case BO_LAnd: + case BO_LOr: + default: + return this->bail(BO); + } + + llvm_unreachable("Unhandled binary op"); +} + +/// Perform addition/subtraction of a pointer and an integer or +/// subtraction of two pointers. +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitPointerArithBinOp(const BinaryOperator *E) { + BinaryOperatorKind Op = E->getOpcode(); + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); + + if ((Op != BO_Add && Op != BO_Sub) || + (!LHS->getType()->isPointerType() && !RHS->getType()->isPointerType())) + return false; + + std::optional<PrimType> LT = classify(LHS); + std::optional<PrimType> RT = classify(RHS); + + if (!LT || !RT) + return false; + + if (LHS->getType()->isPointerType() && RHS->getType()->isPointerType()) { + if (Op != BO_Sub) + return false; + + assert(E->getType()->isIntegerType()); + if (!visit(RHS) || !visit(LHS)) + return false; + + return this->emitSubPtr(classifyPrim(E->getType()), E); + } + + PrimType OffsetType; + if (LHS->getType()->isIntegerType()) { + if (!visit(RHS) || !visit(LHS)) + return false; + OffsetType = *LT; + } else if (RHS->getType()->isIntegerType()) { + if (!visit(LHS) || !visit(RHS)) + return false; + OffsetType = *RT; + } else { + return false; + } + + if (Op == BO_Add) + return this->emitAddOffset(OffsetType, E); + else if (Op == BO_Sub) + return this->emitSubOffset(OffsetType, E); + + return this->bail(E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E) { + if (std::optional<PrimType> T = classify(E)) + return this->emitZero(*T, E); + + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitArraySubscriptExpr( + const ArraySubscriptExpr *E) { + const Expr *Base = E->getBase(); + const Expr *Index = E->getIdx(); + PrimType IndexT = classifyPrim(Index->getType()); + + // Take pointer of LHS, add offset from RHS, narrow result. + // What's left on the stack after this is a pointer. + if (!this->visit(Base)) + return false; + + if (!this->visit(Index)) + return false; + + if (!this->emitAddOffset(IndexT, E)) + return false; + + if (!this->emitNarrowPtr(E)) + return false; + + if (DiscardResult) + return this->emitPopPtr(E); + + return true; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitInitListExpr(const InitListExpr *E) { + for (const Expr *Init : E->inits()) { + if (!this->visit(Init)) return false; + } + return true; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitSubstNonTypeTemplateParmExpr( + const SubstNonTypeTemplateParmExpr *E) { + return this->visit(E->getReplacement()); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitConstantExpr(const ConstantExpr *E) { + // TODO: Check if the ConstantExpr already has a value set and if so, + // use that instead of evaluating it again. + return this->visit(E->getSubExpr()); +} + +static CharUnits AlignOfType(QualType T, const ASTContext &ASTCtx, + UnaryExprOrTypeTrait Kind) { + bool AlignOfReturnsPreferred = + ASTCtx.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver7; - auto Discard = [this, T, BO](bool Result) { - if (!Result) + // C++ [expr.alignof]p3: + // When alignof is applied to a reference type, the result is the + // alignment of the referenced type. + if (const auto *Ref = T->getAs<ReferenceType>()) + T = Ref->getPointeeType(); + + // __alignof is defined to return the preferred alignment. + // Before 8, clang returned the preferred alignment for alignof and + // _Alignof as well. + if (Kind == UETT_PreferredAlignOf || AlignOfReturnsPreferred) + return ASTCtx.toCharUnitsFromBits(ASTCtx.getPreferredTypeAlign(T)); + + return ASTCtx.getTypeAlignInChars(T); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitUnaryExprOrTypeTraitExpr( + const UnaryExprOrTypeTraitExpr *E) { + UnaryExprOrTypeTrait Kind = E->getKind(); + ASTContext &ASTCtx = Ctx.getASTContext(); + + if (Kind == UETT_SizeOf) { + QualType ArgType = E->getTypeOfArgument(); + CharUnits Size; + if (ArgType->isVoidType() || ArgType->isFunctionType()) + Size = CharUnits::One(); + else { + if (ArgType->isDependentType() || !ArgType->isConstantSizeType()) return false; - return DiscardResult ? this->emitPop(*T, BO) : true; - }; - - switch (BO->getOpcode()) { - case BO_EQ: - return Discard(this->emitEQ(*LT, BO)); - case BO_NE: - return Discard(this->emitNE(*LT, BO)); - case BO_LT: - return Discard(this->emitLT(*LT, BO)); - case BO_LE: - return Discard(this->emitLE(*LT, BO)); - case BO_GT: - return Discard(this->emitGT(*LT, BO)); - case BO_GE: - return Discard(this->emitGE(*LT, BO)); - case BO_Sub: - return Discard(this->emitSub(*T, BO)); - case BO_Add: - return Discard(this->emitAdd(*T, BO)); - case BO_Mul: - return Discard(this->emitMul(*T, BO)); - default: - return this->bail(BO); + + Size = ASTCtx.getTypeSizeInChars(ArgType); } + + return this->emitConst(Size.getQuantity(), E); + } + + if (Kind == UETT_AlignOf || Kind == UETT_PreferredAlignOf) { + CharUnits Size; + + if (E->isArgumentType()) { + QualType ArgType = E->getTypeOfArgument(); + + Size = AlignOfType(ArgType, ASTCtx, Kind); + } else { + // Argument is an expression, not a type. + const Expr *Arg = E->getArgumentExpr()->IgnoreParens(); + + // The kinds of expressions that we have special-case logic here for + // should be kept up to date with the special checks for those + // expressions in Sema. + + // alignof decl is always accepted, even if it doesn't make sense: we + // default to 1 in those cases. + if (const auto *DRE = dyn_cast<DeclRefExpr>(Arg)) + Size = ASTCtx.getDeclAlign(DRE->getDecl(), + /*RefAsPointee*/ true); + else if (const auto *ME = dyn_cast<MemberExpr>(Arg)) + Size = ASTCtx.getDeclAlign(ME->getMemberDecl(), + /*RefAsPointee*/ true); + else + Size = AlignOfType(Arg->getType(), ASTCtx, Kind); + } + + return this->emitConst(Size.getQuantity(), E); } - return this->bail(BO); + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitMemberExpr(const MemberExpr *E) { + if (DiscardResult) + return true; + + // 'Base.Member' + const Expr *Base = E->getBase(); + const ValueDecl *Member = E->getMemberDecl(); + + if (!this->visit(Base)) + return false; + + // Base above gives us a pointer on the stack. + // TODO: Implement non-FieldDecl members. + if (const auto *FD = dyn_cast<FieldDecl>(Member)) { + const RecordDecl *RD = FD->getParent(); + const Record *R = getRecord(RD); + const Record::Field *F = R->getField(FD); + // Leave a pointer to the field on the stack. + if (F->Decl->getType()->isReferenceType()) + return this->emitGetFieldPop(PT_Ptr, F->Offset, E); + return this->emitGetPtrField(F->Offset, E); + } + + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitArrayInitIndexExpr( + const ArrayInitIndexExpr *E) { + // ArrayIndex might not be set if a ArrayInitIndexExpr is being evaluated + // stand-alone, e.g. via EvaluateAsInt(). + if (!ArrayIndex) + return false; + return this->emitConst(*ArrayIndex, E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitOpaqueValueExpr(const OpaqueValueExpr *E) { + return this->visit(E->getSourceExpr()); } template <class Emitter> -bool ByteCodeExprGen<Emitter>::discard(const Expr *E) { +bool ByteCodeExprGen<Emitter>::VisitAbstractConditionalOperator( + const AbstractConditionalOperator *E) { + const Expr *Condition = E->getCond(); + const Expr *TrueExpr = E->getTrueExpr(); + const Expr *FalseExpr = E->getFalseExpr(); + + LabelTy LabelEnd = this->getLabel(); // Label after the operator. + LabelTy LabelFalse = this->getLabel(); // Label for the false expr. + + if (!this->visit(Condition)) + return false; + if (!this->jumpFalse(LabelFalse)) + return false; + + if (!this->visit(TrueExpr)) + return false; + if (!this->jump(LabelEnd)) + return false; + + this->emitLabel(LabelFalse); + + if (!this->visit(FalseExpr)) + return false; + + this->fallthrough(LabelEnd); + this->emitLabel(LabelEnd); + + return true; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitStringLiteral(const StringLiteral *E) { + unsigned StringIndex = P.createGlobalString(E); + return this->emitGetPtrGlobal(StringIndex, E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCharacterLiteral( + const CharacterLiteral *E) { + return this->emitConst(E->getValue(), E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCompoundAssignOperator( + const CompoundAssignOperator *E) { + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); + std::optional<PrimType> LT = classify(E->getLHS()->getType()); + std::optional<PrimType> RT = classify(E->getRHS()->getType()); + + if (!LT || !RT) + return false; + + assert(!E->getType()->isPointerType() && + "Support pointer arithmethic in compound assignment operators"); + + // Get LHS pointer, load its value and get RHS value. + if (!visit(LHS)) + return false; + if (!this->emitLoad(*LT, E)) + return false; + if (!visit(RHS)) + return false; + + // Perform operation. + switch (E->getOpcode()) { + case BO_AddAssign: + if (!this->emitAdd(*LT, E)) + return false; + break; + case BO_SubAssign: + if (!this->emitSub(*LT, E)) + return false; + break; + + case BO_MulAssign: + case BO_DivAssign: + case BO_RemAssign: + case BO_ShlAssign: + if (!this->emitShl(*LT, *RT, E)) + return false; + break; + case BO_ShrAssign: + if (!this->emitShr(*LT, *RT, E)) + return false; + break; + case BO_AndAssign: + case BO_XorAssign: + case BO_OrAssign: + default: + llvm_unreachable("Unimplemented compound assign operator"); + } + + // And store the result in LHS. + if (DiscardResult) + return this->emitStorePop(*LT, E); + return this->emitStore(*LT, E); +} + +template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) { OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/true); return this->Visit(E); } @@ -219,7 +563,7 @@ bool ByteCodeExprGen<Emitter>::visit(const Expr *E) { template <class Emitter> bool ByteCodeExprGen<Emitter>::visitBool(const Expr *E) { - if (Optional<PrimType> T = classify(E->getType())) { + if (std::optional<PrimType> T = classify(E->getType())) { return visit(E); } else { return this->bail(E); @@ -257,7 +601,7 @@ template <class Emitter> bool ByteCodeExprGen<Emitter>::dereference( const Expr *LV, DerefKind AK, llvm::function_ref<bool(PrimType)> Direct, llvm::function_ref<bool(PrimType)> Indirect) { - if (Optional<PrimType> T = classify(LV->getType())) { + if (std::optional<PrimType> T = classify(LV->getType())) { if (!LV->refersToBitField()) { // Only primitive, non bit-field types can be dereferenced directly. if (auto *DE = dyn_cast<DeclRefExpr>(LV)) { @@ -350,7 +694,7 @@ bool ByteCodeExprGen<Emitter>::dereferenceVar( return false; return DiscardResult ? true : this->emitGetPtrLocal(L.Offset, LV); } - } else if (auto Idx = getGlobalIdx(VD)) { + } else if (auto Idx = P.getGlobal(VD)) { switch (AK) { case DerefKind::Read: if (!this->emitGetGlobal(T, *Idx, LV)) @@ -382,7 +726,7 @@ bool ByteCodeExprGen<Emitter>::dereferenceVar( if (VD->hasLocalStorage() && VD->hasInit() && !VD->isConstexpr()) { QualType VT = VD->getType(); if (VT.isConstQualified() && VT->isFundamentalType()) - return this->Visit(VD->getInit()); + return this->visit(VD->getInit()); } } @@ -391,27 +735,27 @@ bool ByteCodeExprGen<Emitter>::dereferenceVar( } template <class Emitter> -bool ByteCodeExprGen<Emitter>::emitConst(PrimType T, unsigned NumBits, - const APInt &Value, const Expr *E) { - switch (T) { +template <typename T> +bool ByteCodeExprGen<Emitter>::emitConst(T Value, const Expr *E) { + switch (classifyPrim(E->getType())) { case PT_Sint8: - return this->emitConstSint8(Value.getSExtValue(), E); + return this->emitConstSint8(Value, E); case PT_Uint8: - return this->emitConstUint8(Value.getZExtValue(), E); + return this->emitConstUint8(Value, E); case PT_Sint16: - return this->emitConstSint16(Value.getSExtValue(), E); + return this->emitConstSint16(Value, E); case PT_Uint16: - return this->emitConstUint16(Value.getZExtValue(), E); + return this->emitConstUint16(Value, E); case PT_Sint32: - return this->emitConstSint32(Value.getSExtValue(), E); + return this->emitConstSint32(Value, E); case PT_Uint32: - return this->emitConstUint32(Value.getZExtValue(), E); + return this->emitConstUint32(Value, E); case PT_Sint64: - return this->emitConstSint64(Value.getSExtValue(), E); + return this->emitConstSint64(Value, E); case PT_Uint64: - return this->emitConstUint64(Value.getZExtValue(), E); + return this->emitConstUint64(Value, E); case PT_Bool: - return this->emitConstBool(Value.getBoolValue(), E); + return this->emitConstBool(Value, E); case PT_Ptr: llvm_unreachable("Invalid integral type"); break; @@ -420,11 +764,29 @@ bool ByteCodeExprGen<Emitter>::emitConst(PrimType T, unsigned NumBits, } template <class Emitter> +bool ByteCodeExprGen<Emitter>::emitConst(const APSInt &Value, const Expr *E) { + if (Value.isSigned()) + return this->emitConst(Value.getSExtValue(), E); + return this->emitConst(Value.getZExtValue(), E); +} + +template <class Emitter> unsigned ByteCodeExprGen<Emitter>::allocateLocalPrimitive(DeclTy &&Src, PrimType Ty, bool IsConst, bool IsExtended) { - Descriptor *D = P.createDescriptor(Src, Ty, IsConst, Src.is<const Expr *>()); + // Make sure we don't accidentally register the same decl twice. + if (const auto *VD = + dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) { + assert(!P.getGlobal(VD)); + assert(Locals.find(VD) == Locals.end()); + } + + // FIXME: There are cases where Src.is<Expr*>() is wrong, e.g. + // (int){12} in C. Consider using Expr::isTemporaryObject() instead + // or isa<MaterializeTemporaryExpr>(). + Descriptor *D = P.createDescriptor(Src, Ty, Descriptor::InlineDescMD, IsConst, + Src.is<const Expr *>()); Scope::Local Local = this->createLocal(D); if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>())) Locals.insert({VD, Local}); @@ -433,23 +795,34 @@ unsigned ByteCodeExprGen<Emitter>::allocateLocalPrimitive(DeclTy &&Src, } template <class Emitter> -llvm::Optional<unsigned> +std::optional<unsigned> ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) { - QualType Ty; + // Make sure we don't accidentally register the same decl twice. + if (const auto *VD = + dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) { + assert(!P.getGlobal(VD)); + assert(Locals.find(VD) == Locals.end()); + } + QualType Ty; const ValueDecl *Key = nullptr; + const Expr *Init = nullptr; bool IsTemporary = false; - if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>())) { + if (auto *VD = dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) { Key = VD; Ty = VD->getType(); + + if (const auto *VarD = dyn_cast<VarDecl>(VD)) + Init = VarD->getInit(); } if (auto *E = Src.dyn_cast<const Expr *>()) { IsTemporary = true; Ty = E->getType(); } - Descriptor *D = P.createDescriptor(Src, Ty.getTypePtr(), - Ty.isConstQualified(), IsTemporary); + Descriptor *D = P.createDescriptor( + Src, Ty.getTypePtr(), Descriptor::InlineDescMD, Ty.isConstQualified(), + IsTemporary, /*IsMutable=*/false, Init); if (!D) return {}; @@ -460,38 +833,230 @@ ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) { return Local.Offset; } +// NB: When calling this function, we have a pointer to the +// array-to-initialize on the stack. template <class Emitter> -bool ByteCodeExprGen<Emitter>::visitInitializer( - const Expr *Init, InitFnRef InitFn) { - OptionScope<Emitter> Scope(this, InitFn); - return this->Visit(Init); +bool ByteCodeExprGen<Emitter>::visitArrayInitializer(const Expr *Initializer) { + assert(Initializer->getType()->isArrayType()); + + // TODO: Fillers? + if (const auto *InitList = dyn_cast<InitListExpr>(Initializer)) { + unsigned ElementIndex = 0; + for (const Expr *Init : InitList->inits()) { + if (std::optional<PrimType> T = classify(Init->getType())) { + // Visit the primitive element like normal. + if (!this->emitDupPtr(Init)) + return false; + if (!this->visit(Init)) + return false; + if (!this->emitInitElem(*T, ElementIndex, Init)) + return false; + } else { + // Advance the pointer currently on the stack to the given + // dimension and narrow(). + if (!this->emitDupPtr(Init)) + return false; + if (!this->emitConstUint32(ElementIndex, Init)) + return false; + if (!this->emitAddOffsetUint32(Init)) + return false; + if (!this->emitNarrowPtr(Init)) + return false; + + if (!visitInitializer(Init)) + return false; + } + if (!this->emitPopPtr(Init)) + return false; + + ++ElementIndex; + } + return true; + } else if (const auto *DIE = dyn_cast<CXXDefaultInitExpr>(Initializer)) { + return this->visitInitializer(DIE->getExpr()); + } else if (const auto *AILE = dyn_cast<ArrayInitLoopExpr>(Initializer)) { + // TODO: This compiles to quite a lot of bytecode if the array is larger. + // Investigate compiling this to a loop, or at least try to use + // the AILE's Common expr. + const Expr *SubExpr = AILE->getSubExpr(); + size_t Size = AILE->getArraySize().getZExtValue(); + std::optional<PrimType> ElemT = classify(SubExpr->getType()); + + // So, every iteration, we execute an assignment here + // where the LHS is on the stack (the target array) + // and the RHS is our SubExpr. + for (size_t I = 0; I != Size; ++I) { + ArrayIndexScope<Emitter> IndexScope(this, I); + + if (!this->emitDupPtr(SubExpr)) // LHS + return false; + + if (ElemT) { + if (!this->visit(SubExpr)) + return false; + if (!this->emitInitElem(*ElemT, I, Initializer)) + return false; + } else { + // Narrow to our array element and recurse into visitInitializer() + if (!this->emitConstUint64(I, SubExpr)) + return false; + + if (!this->emitAddOffsetUint64(SubExpr)) + return false; + + if (!this->emitNarrowPtr(SubExpr)) + return false; + + if (!visitInitializer(SubExpr)) + return false; + } + + if (!this->emitPopPtr(Initializer)) + return false; + } + return true; + } else if (const auto *IVIE = dyn_cast<ImplicitValueInitExpr>(Initializer)) { + const ArrayType *AT = IVIE->getType()->getAsArrayTypeUnsafe(); + assert(AT); + const auto *CAT = cast<ConstantArrayType>(AT); + size_t NumElems = CAT->getSize().getZExtValue(); + + if (std::optional<PrimType> ElemT = classify(CAT->getElementType())) { + // TODO(perf): For int and bool types, we can probably just skip this + // since we memset our Block*s to 0 and so we have the desired value + // without this. + for (size_t I = 0; I != NumElems; ++I) { + if (!this->emitZero(*ElemT, Initializer)) + return false; + if (!this->emitInitElem(*ElemT, I, Initializer)) + return false; + } + } else { + assert(false && "default initializer for non-primitive type"); + } + + return true; + } else if (const auto *Ctor = dyn_cast<CXXConstructExpr>(Initializer)) { + const ConstantArrayType *CAT = + Ctx.getASTContext().getAsConstantArrayType(Ctor->getType()); + assert(CAT); + size_t NumElems = CAT->getSize().getZExtValue(); + const Function *Func = getFunction(Ctor->getConstructor()); + if (!Func || !Func->isConstexpr()) + return false; + + // FIXME(perf): We're calling the constructor once per array element here, + // in the old intepreter we had a special-case for trivial constructors. + for (size_t I = 0; I != NumElems; ++I) { + if (!this->emitDupPtr(Initializer)) + return false; + if (!this->emitConstUint64(I, Initializer)) + return false; + if (!this->emitAddOffsetUint64(Initializer)) + return false; + if (!this->emitNarrowPtr(Initializer)) + return false; + + // Constructor arguments. + for (const auto *Arg : Ctor->arguments()) { + if (!this->visit(Arg)) + return false; + } + + if (!this->emitCall(Func, Initializer)) + return false; + } + return true; + } + + assert(false && "Unknown expression for array initialization"); + return false; } template <class Emitter> -bool ByteCodeExprGen<Emitter>::getPtrVarDecl(const VarDecl *VD, const Expr *E) { - // Generate a pointer to the local, loading refs. - if (Optional<unsigned> Idx = getGlobalIdx(VD)) { - if (VD->getType()->isReferenceType()) - return this->emitGetGlobalPtr(*Idx, E); - else - return this->emitGetPtrGlobal(*Idx, E); +bool ByteCodeExprGen<Emitter>::visitRecordInitializer(const Expr *Initializer) { + Initializer = Initializer->IgnoreParenImpCasts(); + assert(Initializer->getType()->isRecordType()); + + if (const auto CtorExpr = dyn_cast<CXXConstructExpr>(Initializer)) { + const Function *Func = getFunction(CtorExpr->getConstructor()); + + if (!Func || !Func->isConstexpr()) + return false; + + // The This pointer is already on the stack because this is an initializer, + // but we need to dup() so the call() below has its own copy. + if (!this->emitDupPtr(Initializer)) + return false; + + // Constructor arguments. + for (const auto *Arg : CtorExpr->arguments()) { + if (!this->visit(Arg)) + return false; + } + + return this->emitCall(Func, Initializer); + } else if (const auto *InitList = dyn_cast<InitListExpr>(Initializer)) { + const Record *R = getRecord(InitList->getType()); + + unsigned InitIndex = 0; + for (const Expr *Init : InitList->inits()) { + const Record::Field *FieldToInit = R->getField(InitIndex); + + if (!this->emitDupPtr(Initializer)) + return false; + + if (std::optional<PrimType> T = classify(Init)) { + if (!this->visit(Init)) + return false; + + if (!this->emitInitField(*T, FieldToInit->Offset, Initializer)) + return false; + + if (!this->emitPopPtr(Initializer)) + return false; + } else { + // Non-primitive case. Get a pointer to the field-to-initialize + // on the stack and recurse into visitInitializer(). + if (!this->emitGetPtrField(FieldToInit->Offset, Init)) + return false; + + if (!this->visitInitializer(Init)) + return false; + + if (!this->emitPopPtr(Initializer)) + return false; + } + ++InitIndex; + } + + return true; + } else if (const CallExpr *CE = dyn_cast<CallExpr>(Initializer)) { + // RVO functions expect a pointer to initialize on the stack. + // Dup our existing pointer so it has its own copy to use. + if (!this->emitDupPtr(Initializer)) + return false; + + return this->VisitCallExpr(CE); + } else if (const auto *DIE = dyn_cast<CXXDefaultInitExpr>(Initializer)) { + return this->visitInitializer(DIE->getExpr()); } - return this->bail(VD); + + return false; } template <class Emitter> -llvm::Optional<unsigned> -ByteCodeExprGen<Emitter>::getGlobalIdx(const VarDecl *VD) { - if (VD->isConstexpr()) { - // Constexpr decl - it must have already been defined. - return P.getGlobal(VD); - } - if (!VD->hasLocalStorage()) { - // Not constexpr, but a global var - can have pointer taken. - Program::DeclScope Scope(P, VD); - return P.getOrCreateGlobal(VD); - } - return {}; +bool ByteCodeExprGen<Emitter>::visitInitializer(const Expr *Initializer) { + QualType InitializerType = Initializer->getType(); + + if (InitializerType->isArrayType()) + return visitArrayInitializer(Initializer); + + if (InitializerType->isRecordType()) + return visitRecordInitializer(Initializer); + + // Otherwise, visit the expression like normal. + return this->visit(Initializer); } template <class Emitter> @@ -516,52 +1081,339 @@ Record *ByteCodeExprGen<Emitter>::getRecord(const RecordDecl *RD) { } template <class Emitter> +const Function *ByteCodeExprGen<Emitter>::getFunction(const FunctionDecl *FD) { + assert(FD); + const Function *Func = P.getFunction(FD); + bool IsBeingCompiled = Func && !Func->isFullyCompiled(); + bool WasNotDefined = Func && !Func->hasBody(); + + if (IsBeingCompiled) + return Func; + + if (!Func || WasNotDefined) { + if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(Ctx, P).compileFunc(FD)) + Func = *R; + else { + llvm::consumeError(R.takeError()); + return nullptr; + } + } + + return Func; +} + +template <class Emitter> bool ByteCodeExprGen<Emitter>::visitExpr(const Expr *Exp) { ExprScope<Emitter> RootScope(this); if (!visit(Exp)) return false; - if (Optional<PrimType> T = classify(Exp)) + if (std::optional<PrimType> T = classify(Exp)) return this->emitRet(*T, Exp); else return this->emitRetValue(Exp); } +/// Toplevel visitDecl(). +/// We get here from evaluateAsInitializer(). +/// We need to evaluate the initializer and return its value. template <class Emitter> bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD) { + std::optional<PrimType> VarT = classify(VD->getType()); + + // Create and initialize the variable. + if (!this->visitVarDecl(VD)) + return false; + + // Get a pointer to the variable + if (shouldBeGloballyIndexed(VD)) { + auto GlobalIndex = P.getGlobal(VD); + assert(GlobalIndex); // visitVarDecl() didn't return false. + if (!this->emitGetPtrGlobal(*GlobalIndex, VD)) + return false; + } else { + auto Local = Locals.find(VD); + assert(Local != Locals.end()); // Same here. + if (!this->emitGetPtrLocal(Local->second.Offset, VD)) + return false; + } + + // Return the value + if (VarT) { + if (!this->emitLoadPop(*VarT, VD)) + return false; + + return this->emitRet(*VarT, VD); + } + + return this->emitRetValue(VD); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::visitVarDecl(const VarDecl *VD) { const Expr *Init = VD->getInit(); + std::optional<PrimType> VarT = classify(VD->getType()); + + if (shouldBeGloballyIndexed(VD)) { + std::optional<unsigned> GlobalIndex = P.getOrCreateGlobal(VD, Init); + + if (!GlobalIndex) + return this->bail(VD); + + assert(Init); + { + DeclScope<Emitter> LocalScope(this, VD); - if (Optional<unsigned> I = P.createGlobal(VD)) { - if (Optional<PrimType> T = classify(VD->getType())) { - { - // Primitive declarations - compute the value and set it. - DeclScope<Emitter> LocalScope(this, VD); - if (!visit(Init)) + if (VarT) { + if (!this->visit(Init)) return false; + return this->emitInitGlobal(*VarT, *GlobalIndex, VD); } + return this->visitGlobalInitializer(Init, *GlobalIndex); + } + } else { + VariableScope<Emitter> LocalScope(this); + if (VarT) { + unsigned Offset = this->allocateLocalPrimitive( + VD, *VarT, VD->getType().isConstQualified()); + if (Init) { + // Compile the initializer in its own scope. + ExprScope<Emitter> Scope(this); + if (!this->visit(Init)) + return false; - // If the declaration is global, save the value for later use. - if (!this->emitDup(*T, VD)) - return false; - if (!this->emitInitGlobal(*T, *I, VD)) - return false; - return this->emitRet(*T, VD); + return this->emitSetLocal(*VarT, Offset, VD); + } } else { - { - // Composite declarations - allocate storage and initialize it. - DeclScope<Emitter> LocalScope(this, VD); - if (!visitGlobalInitializer(Init, *I)) + if (std::optional<unsigned> Offset = this->allocateLocal(VD)) { + if (Init) + return this->visitLocalInitializer(Init, *Offset); + } + } + return true; + } + + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) { + assert(!E->getBuiltinCallee() && "Builtin functions aren't supported yet"); + + const Decl *Callee = E->getCalleeDecl(); + if (const auto *FuncDecl = dyn_cast_or_null<FunctionDecl>(Callee)) { + const Function *Func = getFunction(FuncDecl); + if (!Func) + return false; + // If the function is being compiled right now, this is a recursive call. + // In that case, the function can't be valid yet, even though it will be + // later. + // If the function is already fully compiled but not constexpr, it was + // found to be faulty earlier on, so bail out. + if (Func->isFullyCompiled() && !Func->isConstexpr()) + return false; + + QualType ReturnType = E->getCallReturnType(Ctx.getASTContext()); + std::optional<PrimType> T = classify(ReturnType); + + if (Func->hasRVO() && DiscardResult) { + // If we need to discard the return value but the function returns its + // value via an RVO pointer, we need to create one such pointer just + // for this call. + if (std::optional<unsigned> LocalIndex = allocateLocal(E)) { + if (!this->emitGetPtrLocal(*LocalIndex, E)) return false; } + } - // Return a pointer to the global. - if (!this->emitGetPtrGlobal(*I, VD)) + // Put arguments on the stack. + for (const auto *Arg : E->arguments()) { + if (!this->visit(Arg)) return false; - return this->emitRetValue(VD); } + + // In any case call the function. The return value will end up on the stack and + // if the function has RVO, we already have the pointer on the stack to write + // the result into. + if (!this->emitCall(Func, E)) + return false; + + if (DiscardResult && !ReturnType->isVoidType() && T) + return this->emitPop(*T, E); + + return true; + } else { + assert(false && "We don't support non-FunctionDecl callees right now."); } - return this->bail(VD); + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCXXMemberCallExpr( + const CXXMemberCallExpr *E) { + // Get a This pointer on the stack. + if (!this->visit(E->getImplicitObjectArgument())) + return false; + + return VisitCallExpr(E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCXXDefaultInitExpr( + const CXXDefaultInitExpr *E) { + return this->visit(E->getExpr()); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCXXDefaultArgExpr( + const CXXDefaultArgExpr *E) { + return this->visit(E->getExpr()); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCXXBoolLiteralExpr( + const CXXBoolLiteralExpr *E) { + if (DiscardResult) + return true; + + return this->emitConstBool(E->getValue(), E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCXXNullPtrLiteralExpr( + const CXXNullPtrLiteralExpr *E) { + if (DiscardResult) + return true; + + return this->emitNullPtr(E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCXXThisExpr(const CXXThisExpr *E) { + if (DiscardResult) + return true; + return this->emitThis(E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) { + const Expr *SubExpr = E->getSubExpr(); + std::optional<PrimType> T = classify(SubExpr->getType()); + + // TODO: Support pointers for inc/dec operators. + switch (E->getOpcode()) { + case UO_PostInc: { // x++ + if (!this->visit(SubExpr)) + return false; + + return DiscardResult ? this->emitIncPop(*T, E) : this->emitInc(*T, E); + } + case UO_PostDec: { // x-- + if (!this->visit(SubExpr)) + return false; + + return DiscardResult ? this->emitDecPop(*T, E) : this->emitDec(*T, E); + } + case UO_PreInc: { // ++x + if (!this->visit(SubExpr)) + return false; + + // Post-inc and pre-inc are the same if the value is to be discarded. + if (DiscardResult) + return this->emitIncPop(*T, E); + + this->emitLoad(*T, E); + this->emitConst(1, E); + this->emitAdd(*T, E); + return this->emitStore(*T, E); + } + case UO_PreDec: { // --x + if (!this->visit(SubExpr)) + return false; + + // Post-dec and pre-dec are the same if the value is to be discarded. + if (DiscardResult) + return this->emitDecPop(*T, E); + + this->emitLoad(*T, E); + this->emitConst(1, E); + this->emitSub(*T, E); + return this->emitStore(*T, E); + } + case UO_LNot: // !x + if (!this->visit(SubExpr)) + return false; + // The Inv doesn't change anything, so skip it if we don't need the result. + return DiscardResult ? this->emitPop(*T, E) : this->emitInvBool(E); + case UO_Minus: // -x + if (!this->visit(SubExpr)) + return false; + return DiscardResult ? this->emitPop(*T, E) : this->emitNeg(*T, E); + case UO_Plus: // +x + if (!this->visit(SubExpr)) // noop + return false; + return DiscardResult ? this->emitPop(*T, E) : true; + case UO_AddrOf: // &x + // We should already have a pointer when we get here. + if (!this->visit(SubExpr)) + return false; + return DiscardResult ? this->emitPop(*T, E) : true; + case UO_Deref: // *x + return dereference( + SubExpr, DerefKind::Read, + [](PrimType) { + llvm_unreachable("Dereferencing requires a pointer"); + return false; + }, + [this, E](PrimType T) { + return DiscardResult ? this->emitPop(T, E) : true; + }); + case UO_Not: // ~x + if (!this->visit(SubExpr)) + return false; + return DiscardResult ? this->emitPop(*T, E) : this->emitComp(*T, E); + case UO_Real: // __real x + case UO_Imag: // __imag x + case UO_Extension: + case UO_Coawait: + assert(false && "Unhandled opcode"); + } + + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) { + const auto *Decl = E->getDecl(); + // References are implemented via pointers, so when we see a DeclRefExpr + // pointing to a reference, we need to get its value directly (i.e. the + // pointer to the actual value) instead of a pointer to the pointer to the + // value. + bool IsReference = Decl->getType()->isReferenceType(); + + if (auto It = Locals.find(Decl); It != Locals.end()) { + const unsigned Offset = It->second.Offset; + + if (IsReference) + return this->emitGetLocal(PT_Ptr, Offset, E); + return this->emitGetPtrLocal(Offset, E); + } else if (auto GlobalIndex = P.getGlobal(Decl)) { + if (IsReference) + return this->emitGetGlobal(PT_Ptr, *GlobalIndex, E); + + return this->emitGetPtrGlobal(*GlobalIndex, E); + } else if (const auto *PVD = dyn_cast<ParmVarDecl>(Decl)) { + if (auto It = this->Params.find(PVD); It != this->Params.end()) { + if (IsReference) + return this->emitGetParam(PT_Ptr, It->second, E); + return this->emitGetPtrParam(It->second, E); + } + } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(Decl)) { + return this->emitConst(ECD->getInitVal(), E); + } + + return false; } template <class Emitter> diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.h b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.h index 82aa413dabbc..c7fcc59e5a60 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -22,7 +22,6 @@ #include "clang/AST/Expr.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/TargetInfo.h" -#include "llvm/ADT/Optional.h" namespace clang { class QualType; @@ -34,25 +33,17 @@ template <class Emitter> class RecordScope; template <class Emitter> class VariableScope; template <class Emitter> class DeclScope; template <class Emitter> class OptionScope; +template <class Emitter> class ArrayIndexScope; /// Compilation context for expressions. template <class Emitter> class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>, public Emitter { protected: - // Emitters for opcodes of various arities. - using NullaryFn = bool (ByteCodeExprGen::*)(const SourceInfo &); - using UnaryFn = bool (ByteCodeExprGen::*)(PrimType, const SourceInfo &); - using BinaryFn = bool (ByteCodeExprGen::*)(PrimType, PrimType, - const SourceInfo &); - // Aliases for types defined in the emitter. using LabelTy = typename Emitter::LabelTy; using AddrTy = typename Emitter::AddrTy; - // Reference to a function generating the pointer of an initialized object.s - using InitFnRef = std::function<bool()>; - /// Current compilation context. Context &Ctx; /// Program to link to. @@ -64,11 +55,34 @@ public: ByteCodeExprGen(Context &Ctx, Program &P, Tys &&... Args) : Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {} - // Expression visitors - result returned on stack. + // Expression visitors - result returned on interp stack. bool VisitCastExpr(const CastExpr *E); bool VisitIntegerLiteral(const IntegerLiteral *E); bool VisitParenExpr(const ParenExpr *E); bool VisitBinaryOperator(const BinaryOperator *E); + bool VisitPointerArithBinOp(const BinaryOperator *E); + bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E); + bool VisitCallExpr(const CallExpr *E); + bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *E); + bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E); + bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E); + bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E); + bool VisitCXXThisExpr(const CXXThisExpr *E); + bool VisitUnaryOperator(const UnaryOperator *E); + bool VisitDeclRefExpr(const DeclRefExpr *E); + bool VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E); + bool VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E); + bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E); + bool VisitInitListExpr(const InitListExpr *E); + bool VisitConstantExpr(const ConstantExpr *E); + bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E); + bool VisitMemberExpr(const MemberExpr *E); + bool VisitArrayInitIndexExpr(const ArrayInitIndexExpr *E); + bool VisitOpaqueValueExpr(const OpaqueValueExpr *E); + bool VisitAbstractConditionalOperator(const AbstractConditionalOperator *E); + bool VisitStringLiteral(const StringLiteral *E); + bool VisitCharacterLiteral(const CharacterLiteral *E); + bool VisitCompoundAssignOperator(const CompoundAssignOperator *E); protected: bool visitExpr(const Expr *E) override; @@ -85,31 +99,18 @@ protected: Record *getRecord(QualType Ty); Record *getRecord(const RecordDecl *RD); - /// Returns the size int bits of an integer. - unsigned getIntWidth(QualType Ty) { - auto &ASTContext = Ctx.getASTContext(); - return ASTContext.getIntWidth(Ty); - } - - /// Returns the value of CHAR_BIT. - unsigned getCharBit() const { - auto &ASTContext = Ctx.getASTContext(); - return ASTContext.getTargetInfo().getCharWidth(); - } + // Returns a function for the given FunctionDecl. + // If the function does not exist yet, it is compiled. + const Function *getFunction(const FunctionDecl *FD); /// Classifies a type. - llvm::Optional<PrimType> classify(const Expr *E) const { + std::optional<PrimType> classify(const Expr *E) const { return E->isGLValue() ? PT_Ptr : classify(E->getType()); } - llvm::Optional<PrimType> classify(QualType Ty) const { + std::optional<PrimType> classify(QualType Ty) const { return Ctx.classify(Ty); } - /// Checks if a pointer needs adjustment. - bool needsAdjust(QualType Ty) const { - return true; - } - /// Classifies a known primitive type PrimType classifyPrim(QualType Ty) const { if (auto T = classify(Ty)) { @@ -122,29 +123,49 @@ protected: bool discard(const Expr *E); /// Evaluates an expression and places result on stack. bool visit(const Expr *E); - /// Compiles an initializer for a local. - bool visitInitializer(const Expr *E, InitFnRef GenPtr); + /// Compiles an initializer. + bool visitInitializer(const Expr *E); + /// Compiles an array initializer. + bool visitArrayInitializer(const Expr *Initializer); + /// Compiles a record initializer. + bool visitRecordInitializer(const Expr *Initializer); + /// Creates and initializes a variable from the given decl. + bool visitVarDecl(const VarDecl *VD); /// Visits an expression and converts it to a boolean. bool visitBool(const Expr *E); /// Visits an initializer for a local. bool visitLocalInitializer(const Expr *Init, unsigned I) { - return visitInitializer(Init, [this, I, Init] { - return this->emitGetPtrLocal(I, Init); - }); + if (!this->emitGetPtrLocal(I, Init)) + return false; + + if (!visitInitializer(Init)) + return false; + + return this->emitPopPtr(Init); } /// Visits an initializer for a global. bool visitGlobalInitializer(const Expr *Init, unsigned I) { - return visitInitializer(Init, [this, I, Init] { - return this->emitGetPtrGlobal(I, Init); - }); + if (!this->emitGetPtrGlobal(I, Init)) + return false; + + if (!visitInitializer(Init)) + return false; + + return this->emitPopPtr(Init); } /// Visits a delegated initializer. bool visitThisInitializer(const Expr *I) { - return visitInitializer(I, [this, I] { return this->emitThis(I); }); + if (!this->emitThis(I)) + return false; + + if (!visitInitializer(I)) + return false; + + return this->emitPopPtr(I); } /// Creates a local primitive value. @@ -152,8 +173,7 @@ protected: bool IsExtended = false); /// Allocates a space storing a local given its type. - llvm::Optional<unsigned> allocateLocal(DeclTy &&Decl, - bool IsExtended = false); + std::optional<unsigned> allocateLocal(DeclTy &&Decl, bool IsExtended = false); private: friend class VariableScope<Emitter>; @@ -161,6 +181,7 @@ private: friend class RecordScope<Emitter>; friend class DeclScope<Emitter>; friend class OptionScope<Emitter>; + friend class ArrayIndexScope<Emitter>; /// Emits a zero initializer. bool visitZeroInitializer(PrimType T, const Expr *E); @@ -188,28 +209,28 @@ private: DerefKind AK, llvm::function_ref<bool(PrimType)> Direct, llvm::function_ref<bool(PrimType)> Indirect); - /// Emits an APInt constant. - bool emitConst(PrimType T, unsigned NumBits, const llvm::APInt &Value, - const Expr *E); + /// Emits an APSInt constant. + bool emitConst(const APSInt &Value, const Expr *E); + bool emitConst(const APInt &Value, const Expr *E) { + return emitConst(static_cast<APSInt>(Value), E); + } /// Emits an integer constant. - template <typename T> bool emitConst(const Expr *E, T Value) { - QualType Ty = E->getType(); - unsigned NumBits = getIntWidth(Ty); - APInt WrappedValue(NumBits, Value, std::is_signed<T>::value); - return emitConst(*Ctx.classify(Ty), NumBits, WrappedValue, E); + template <typename T> bool emitConst(T Value, const Expr *E); + + /// Returns the CXXRecordDecl for the type of the given expression, + /// or nullptr if no such decl exists. + const CXXRecordDecl *getRecordDecl(const Expr *E) const { + QualType T = E->getType(); + if (const auto *RD = T->getPointeeCXXRecordDecl()) + return RD; + return T->getAsCXXRecordDecl(); } - /// Returns a pointer to a variable declaration. - bool getPtrVarDecl(const VarDecl *VD, const Expr *E); - - /// Returns the index of a global. - llvm::Optional<unsigned> getGlobalIdx(const VarDecl *VD); - - /// Emits the initialized pointer. - bool emitInitFn() { - assert(InitFn && "missing initializer"); - return (*InitFn)(); + /// Returns whether we should create a global variable for the + /// given VarDecl. + bool shouldBeGloballyIndexed(const VarDecl *VD) const { + return VD->hasGlobalStorage() || VD->isConstexpr(); } protected: @@ -222,14 +243,11 @@ protected: /// Current scope. VariableScope<Emitter> *VarScope = nullptr; - /// Current argument index. - llvm::Optional<uint64_t> ArrayIndex; + /// Current argument index. Needed to emit ArrayInitIndexExpr. + std::optional<uint64_t> ArrayIndex; /// Flag indicating if return value is to be discarded. bool DiscardResult = false; - - /// Expression being initialized. - llvm::Optional<InitFnRef> InitFn = {}; }; extern template class ByteCodeExprGen<ByteCodeEmitter>; @@ -238,6 +256,11 @@ extern template class ByteCodeExprGen<EvalEmitter>; /// Scope chain managing the variable lifetimes. template <class Emitter> class VariableScope { public: + VariableScope(ByteCodeExprGen<Emitter> *Ctx) + : Ctx(Ctx), Parent(Ctx->VarScope) { + Ctx->VarScope = this; + } + virtual ~VariableScope() { Ctx->VarScope = this->Parent; } void add(const Scope::Local &Local, bool IsExtended) { @@ -262,11 +285,6 @@ public: VariableScope *getParent() { return Parent; } protected: - VariableScope(ByteCodeExprGen<Emitter> *Ctx) - : Ctx(Ctx), Parent(Ctx->VarScope) { - Ctx->VarScope = this; - } - /// ByteCodeExprGen instance. ByteCodeExprGen<Emitter> *Ctx; /// Link to the parent scope. @@ -300,7 +318,7 @@ public: protected: /// Index of the scope in the chain. - Optional<unsigned> Idx; + std::optional<unsigned> Idx; }; /// Scope for storage declared in a compound statement. @@ -320,10 +338,25 @@ public: ExprScope(ByteCodeExprGen<Emitter> *Ctx) : LocalScope<Emitter>(Ctx) {} void addExtended(const Scope::Local &Local) override { + assert(this->Parent); this->Parent->addLocal(Local); } }; +template <class Emitter> class ArrayIndexScope final { +public: + ArrayIndexScope(ByteCodeExprGen<Emitter> *Ctx, uint64_t Index) : Ctx(Ctx) { + OldArrayIndex = Ctx->ArrayIndex; + Ctx->ArrayIndex = Index; + } + + ~ArrayIndexScope() { Ctx->ArrayIndex = OldArrayIndex; } + +private: + ByteCodeExprGen<Emitter> *Ctx; + std::optional<uint64_t> OldArrayIndex; +}; + } // namespace interp } // namespace clang diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeGenError.h b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeGenError.h index a4fa4917705d..af464b5ed4ab 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeGenError.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeGenError.h @@ -20,19 +20,19 @@ namespace interp { /// Error thrown by the compiler. struct ByteCodeGenError : public llvm::ErrorInfo<ByteCodeGenError> { public: - ByteCodeGenError(SourceLocation Loc) : Loc(Loc) {} - ByteCodeGenError(const Stmt *S) : ByteCodeGenError(S->getBeginLoc()) {} - ByteCodeGenError(const Decl *D) : ByteCodeGenError(D->getBeginLoc()) {} + ByteCodeGenError(SourceRange Range) : Range(Range) {} + ByteCodeGenError(const Stmt *S) : ByteCodeGenError(S->getSourceRange()) {} + ByteCodeGenError(const Decl *D) : ByteCodeGenError(D->getSourceRange()) {} void log(raw_ostream &OS) const override { OS << "unimplemented feature"; } - const SourceLocation &getLoc() const { return Loc; } + const SourceRange &getRange() const { return Range; } static char ID; private: - // Start of the item where the error occurred. - SourceLocation Loc; + // Range of the item where the error occurred. + SourceRange Range; // Users are not expected to use error_code. std::error_code convertToErrorCode() const override { diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.cpp index 90e84149b055..af97c57c98b7 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.cpp @@ -94,11 +94,63 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) { // Classify the return type. ReturnType = this->classify(F->getReturnType()); - // Set up fields and context if a constructor. - if (auto *MD = dyn_cast<CXXMethodDecl>(F)) - return this->bail(MD); + // Constructor. Set up field initializers. + if (const auto Ctor = dyn_cast<CXXConstructorDecl>(F)) { + const RecordDecl *RD = Ctor->getParent(); + const Record *R = this->getRecord(RD); + if (!R) + return false; + + for (const auto *Init : Ctor->inits()) { + const Expr *InitExpr = Init->getInit(); + if (const FieldDecl *Member = Init->getMember()) { + const Record::Field *F = R->getField(Member); + + if (std::optional<PrimType> T = this->classify(InitExpr)) { + if (!this->emitThis(InitExpr)) + return false; + + if (!this->visit(InitExpr)) + return false; + + if (!this->emitInitField(*T, F->Offset, InitExpr)) + return false; + + if (!this->emitPopPtr(InitExpr)) + return false; + } else { + // Non-primitive case. Get a pointer to the field-to-initialize + // on the stack and call visitInitialzer() for it. + if (!this->emitThis(InitExpr)) + return false; + + if (!this->emitGetPtrField(F->Offset, InitExpr)) + return false; + + if (!this->visitInitializer(InitExpr)) + return false; + + if (!this->emitPopPtr(InitExpr)) + return false; + } + } else if (const Type *Base = Init->getBaseClass()) { + // Base class initializer. + // Get This Base and call initializer on it. + auto *BaseDecl = Base->getAsCXXRecordDecl(); + assert(BaseDecl); + const Record::Base *B = R->getBase(BaseDecl); + assert(B); + if (!this->emitGetPtrThisBase(B->Offset, InitExpr)) + return false; + if (!this->visitInitializer(InitExpr)) + return false; + if (!this->emitPopPtr(InitExpr)) + return false; + } + } + } - if (auto *Body = F->getBody()) + if (const auto *Body = F->getBody()) if (!visitStmt(Body)) return false; @@ -120,6 +172,16 @@ bool ByteCodeStmtGen<Emitter>::visitStmt(const Stmt *S) { return visitReturnStmt(cast<ReturnStmt>(S)); case Stmt::IfStmtClass: return visitIfStmt(cast<IfStmt>(S)); + case Stmt::WhileStmtClass: + return visitWhileStmt(cast<WhileStmt>(S)); + case Stmt::DoStmtClass: + return visitDoStmt(cast<DoStmt>(S)); + case Stmt::ForStmtClass: + return visitForStmt(cast<ForStmt>(S)); + case Stmt::BreakStmtClass: + return visitBreakStmt(cast<BreakStmt>(S)); + case Stmt::ContinueStmtClass: + return visitContinueStmt(cast<ContinueStmt>(S)); case Stmt::NullStmtClass: return true; default: { @@ -145,7 +207,7 @@ bool ByteCodeStmtGen<Emitter>::visitDeclStmt(const DeclStmt *DS) { for (auto *D : DS->decls()) { // Variable declarator. if (auto *VD = dyn_cast<VarDecl>(D)) { - if (!visitVarDecl(VD)) + if (!this->visitVarDecl(VD)) return false; continue; } @@ -171,18 +233,21 @@ bool ByteCodeStmtGen<Emitter>::visitReturnStmt(const ReturnStmt *RS) { return this->emitRet(*ReturnType, RS); } else { // RVO - construct the value in the return location. - auto ReturnLocation = [this, RE] { return this->emitGetParamPtr(0, RE); }; - if (!this->visitInitializer(RE, ReturnLocation)) + if (!this->emitRVOPtr(RE)) return false; + if (!this->visitInitializer(RE)) + return false; + if (!this->emitPopPtr(RE)) + return false; + this->emitCleanup(); return this->emitRetVoid(RS); } - } else { - this->emitCleanup(); - if (!this->emitRetVoid(RS)) - return false; - return true; } + + // Void return. + this->emitCleanup(); + return this->emitRetVoid(RS); } template <class Emitter> @@ -231,33 +296,99 @@ bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) { } template <class Emitter> -bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) { - auto DT = VD->getType(); +bool ByteCodeStmtGen<Emitter>::visitWhileStmt(const WhileStmt *S) { + const Expr *Cond = S->getCond(); + const Stmt *Body = S->getBody(); - if (!VD->hasLocalStorage()) { - // No code generation required. - return true; - } + LabelTy CondLabel = this->getLabel(); // Label before the condition. + LabelTy EndLabel = this->getLabel(); // Label after the loop. + LoopScope<Emitter> LS(this, EndLabel, CondLabel); - // Integers, pointers, primitives. - if (Optional<PrimType> T = this->classify(DT)) { - auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified()); - // Compile the initialiser in its own scope. - { - ExprScope<Emitter> Scope(this); - if (!this->visit(VD->getInit())) - return false; - } - // Set the value. - return this->emitSetLocal(*T, Off, VD); - } else { - // Composite types - allocate storage and initialize it. - if (auto Off = this->allocateLocal(VD)) { - return this->visitLocalInitializer(VD->getInit(), *Off); - } else { - return this->bail(VD); - } + this->emitLabel(CondLabel); + if (!this->visitBool(Cond)) + return false; + if (!this->jumpFalse(EndLabel)) + return false; + + if (!this->visitStmt(Body)) + return false; + if (!this->jump(CondLabel)) + return false; + + this->emitLabel(EndLabel); + + return true; +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitDoStmt(const DoStmt *S) { + const Expr *Cond = S->getCond(); + const Stmt *Body = S->getBody(); + + LabelTy StartLabel = this->getLabel(); + LabelTy EndLabel = this->getLabel(); + LabelTy CondLabel = this->getLabel(); + LoopScope<Emitter> LS(this, EndLabel, CondLabel); + + this->emitLabel(StartLabel); + if (!this->visitStmt(Body)) + return false; + this->emitLabel(CondLabel); + if (!this->visitBool(Cond)) + return false; + if (!this->jumpTrue(StartLabel)) + return false; + this->emitLabel(EndLabel); + return true; +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitForStmt(const ForStmt *S) { + // for (Init; Cond; Inc) { Body } + const Stmt *Init = S->getInit(); + const Expr *Cond = S->getCond(); + const Expr *Inc = S->getInc(); + const Stmt *Body = S->getBody(); + + LabelTy EndLabel = this->getLabel(); + LabelTy CondLabel = this->getLabel(); + LabelTy IncLabel = this->getLabel(); + LoopScope<Emitter> LS(this, EndLabel, IncLabel); + + if (Init && !this->visitStmt(Init)) + return false; + this->emitLabel(CondLabel); + if (Cond) { + if (!this->visitBool(Cond)) + return false; + if (!this->jumpFalse(EndLabel)) + return false; } + if (Body && !this->visitStmt(Body)) + return false; + this->emitLabel(IncLabel); + if (Inc && !this->discard(Inc)) + return false; + if (!this->jump(CondLabel)) + return false; + this->emitLabel(EndLabel); + return true; +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitBreakStmt(const BreakStmt *S) { + if (!BreakLabel) + return false; + + return this->jump(*BreakLabel); +} + +template <class Emitter> +bool ByteCodeStmtGen<Emitter>::visitContinueStmt(const ContinueStmt *S) { + if (!ContinueLabel) + return false; + + return this->jump(*ContinueLabel); } namespace clang { diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.h b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.h index 3bc665b84b4d..829e199f827c 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.h @@ -22,7 +22,6 @@ #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/StmtVisitor.h" -#include "llvm/ADT/Optional.h" namespace clang { namespace interp { @@ -33,10 +32,10 @@ template <class Emitter> class LabelScope; /// Compilation context for statements. template <class Emitter> -class ByteCodeStmtGen : public ByteCodeExprGen<Emitter> { +class ByteCodeStmtGen final : public ByteCodeExprGen<Emitter> { using LabelTy = typename Emitter::LabelTy; using AddrTy = typename Emitter::AddrTy; - using OptLabelTy = llvm::Optional<LabelTy>; + using OptLabelTy = std::optional<LabelTy>; using CaseMap = llvm::DenseMap<const SwitchCase *, LabelTy>; public: @@ -58,13 +57,14 @@ private: bool visitDeclStmt(const DeclStmt *DS); bool visitReturnStmt(const ReturnStmt *RS); bool visitIfStmt(const IfStmt *IS); + bool visitWhileStmt(const WhileStmt *S); + bool visitDoStmt(const DoStmt *S); + bool visitForStmt(const ForStmt *S); + bool visitBreakStmt(const BreakStmt *S); + bool visitContinueStmt(const ContinueStmt *S); - /// Compiles a variable declaration. - bool visitVarDecl(const VarDecl *VD); - -private: /// Type of the expression returned by the function. - llvm::Optional<PrimType> ReturnType; + std::optional<PrimType> ReturnType; /// Switch case mapping. CaseMap CaseLabels; diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Context.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Context.cpp index 3bfcdfcd4c58..16471242f328 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Context.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/Context.cpp @@ -27,39 +27,52 @@ Context::Context(ASTContext &Ctx) : Ctx(Ctx), P(new Program(*this)) {} Context::~Context() {} bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) { + assert(Stk.empty()); Function *Func = P->getFunction(FD); - if (!Func) { + if (!Func || !Func->hasBody()) { if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD)) { Func = *R; } else { handleAllErrors(R.takeError(), [&Parent](ByteCodeGenError &Err) { - Parent.FFDiag(Err.getLoc(), diag::err_experimental_clang_interp_failed); + Parent.FFDiag(Err.getRange().getBegin(), + diag::err_experimental_clang_interp_failed) + << Err.getRange(); }); return false; } } - if (!Func->isConstexpr()) - return false; - - APValue Dummy; - return Run(Parent, Func, Dummy); + return Func->isConstexpr(); } bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) { + assert(Stk.empty()); ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result); - return Check(Parent, C.interpretExpr(E)); + if (Check(Parent, C.interpretExpr(E))) { + assert(Stk.empty()); + return true; + } + + Stk.clear(); + return false; } bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD, APValue &Result) { + assert(Stk.empty()); ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result); - return Check(Parent, C.interpretDecl(VD)); + if (Check(Parent, C.interpretDecl(VD))) { + assert(Stk.empty()); + return true; + } + + Stk.clear(); + return false; } const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); } -llvm::Optional<PrimType> Context::classify(QualType T) { +std::optional<PrimType> Context::classify(QualType T) const { if (T->isReferenceType() || T->isPointerType()) { return PT_Ptr; } @@ -112,7 +125,7 @@ unsigned Context::getCharBit() const { bool Context::Run(State &Parent, Function *Func, APValue &Result) { InterpState State(Parent, *P, Stk, *this); - State.Current = new InterpFrame(State, Func, nullptr, {}, {}); + State.Current = new InterpFrame(State, Func, /*Caller=*/nullptr, {}); if (Interpret(State, Result)) return true; Stk.clear(); @@ -123,7 +136,9 @@ bool Context::Check(State &Parent, llvm::Expected<bool> &&Flag) { if (Flag) return *Flag; handleAllErrors(Flag.takeError(), [&Parent](ByteCodeGenError &Err) { - Parent.FFDiag(Err.getLoc(), diag::err_experimental_clang_interp_failed); + Parent.FFDiag(Err.getRange().getBegin(), + diag::err_experimental_clang_interp_failed) + << Err.getRange(); }); return false; } diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Context.h b/contrib/llvm-project/clang/lib/AST/Interp/Context.h index 0627d9fb14f5..e49422e64b87 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Context.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/Context.h @@ -18,7 +18,6 @@ #include "InterpStack.h" #include "clang/AST/APValue.h" -#include "llvm/ADT/PointerIntPair.h" namespace clang { class ASTContext; @@ -33,7 +32,7 @@ class State; enum PrimType : unsigned; /// Holds all information required to evaluate constexpr code in a module. -class Context { +class Context final { public: /// Initialises the constexpr VM. Context(ASTContext &Ctx); @@ -60,7 +59,7 @@ public: unsigned getCharBit() const; /// Classifies an expression. - llvm::Optional<PrimType> classify(QualType T); + std::optional<PrimType> classify(QualType T) const; private: /// Runs a function. @@ -69,7 +68,6 @@ private: /// Checks a result from the interpreter. bool Check(State &Parent, llvm::Expected<bool> &&R); -private: /// Current compilation context. ASTContext &Ctx; /// Interpreter stack, shared across invocations. diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.cpp index 5c1a8a9cf306..04bc8681dd6e 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Descriptor.h" +#include "Boolean.h" #include "Pointer.h" #include "PrimType.h" #include "Record.h" @@ -39,6 +40,11 @@ static void ctorArrayTy(Block *, char *Ptr, bool, bool, bool, Descriptor *D) { template <typename T> static void dtorArrayTy(Block *, char *Ptr, Descriptor *D) { + InitMap *IM = *reinterpret_cast<InitMap **>(Ptr); + if (IM != (InitMap *)-1) + free(IM); + + Ptr += sizeof(InitMap *); for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) { reinterpret_cast<T *>(Ptr)[I].~T(); } @@ -72,9 +78,10 @@ static void ctorArrayDesc(Block *B, char *Ptr, bool IsConst, bool IsMutable, Desc->IsBase = false; Desc->IsActive = IsActive; Desc->IsConst = IsConst || D->IsConst; - Desc->IsMutable = IsMutable || D->IsMutable; + Desc->IsFieldMutable = IsMutable || D->IsMutable; if (auto Fn = D->ElemDesc->CtorFn) - Fn(B, ElemLoc, Desc->IsConst, Desc->IsMutable, IsActive, D->ElemDesc); + Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive, + D->ElemDesc); } } @@ -121,13 +128,14 @@ static void ctorRecord(Block *B, char *Ptr, bool IsConst, bool IsMutable, auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + SubOff) - 1; Desc->Offset = SubOff; Desc->Desc = F; - Desc->IsInitialized = (B->isStatic() || F->IsArray) && !IsBase; + Desc->IsInitialized = F->IsArray && !IsBase; Desc->IsBase = IsBase; Desc->IsActive = IsActive && !IsUnion; Desc->IsConst = IsConst || F->IsConst; - Desc->IsMutable = IsMutable || F->IsMutable; + Desc->IsFieldMutable = IsMutable || F->IsMutable; if (auto Fn = F->CtorFn) - Fn(B, Ptr + SubOff, Desc->IsConst, Desc->IsMutable, Desc->IsActive, F); + Fn(B, Ptr + SubOff, Desc->IsConst, Desc->IsFieldMutable, Desc->IsActive, + F); }; for (const auto &B : D->ElemRecord->bases()) CtorSub(B.Offset, B.Desc, /*isBase=*/true); @@ -178,26 +186,30 @@ static BlockCtorFn getCtorArrayPrim(PrimType Type) { } static BlockDtorFn getDtorArrayPrim(PrimType Type) { - COMPOSITE_TYPE_SWITCH(Type, return dtorArrayTy<T>, return nullptr); + TYPE_SWITCH(Type, return dtorArrayTy<T>); + llvm_unreachable("unknown Expr"); } static BlockMoveFn getMoveArrayPrim(PrimType Type) { COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy<T>, return nullptr); } -Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsConst, - bool IsTemporary, bool IsMutable) - : Source(D), ElemSize(primSize(Type)), Size(ElemSize), AllocSize(Size), - IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), - CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)), - MoveFn(getMovePrim(Type)) { +Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, + bool IsConst, bool IsTemporary, bool IsMutable) + : Source(D), ElemSize(primSize(Type)), Size(ElemSize), + MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), IsConst(IsConst), + IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(getCtorPrim(Type)), + DtorFn(getDtorPrim(Type)), MoveFn(getMovePrim(Type)) { + assert(AllocSize >= Size); assert(Source && "Missing source"); } -Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, - bool IsConst, bool IsTemporary, bool IsMutable) +Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, + size_t NumElems, bool IsConst, bool IsTemporary, + bool IsMutable) : Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems), - AllocSize(align(Size) + sizeof(InitMap *)), IsConst(IsConst), + MDSize(MD.value_or(0)), + AllocSize(align(Size) + sizeof(InitMap *) + MDSize), IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { @@ -206,39 +218,42 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize) - : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), + : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0), AllocSize(alignof(void *)), IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) { assert(Source && "Missing source"); } -Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, - bool IsConst, bool IsTemporary, bool IsMutable) +Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD, + unsigned NumElems, bool IsConst, bool IsTemporary, + bool IsMutable) : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), - Size(ElemSize * NumElems), - AllocSize(std::max<size_t>(alignof(void *), Size)), ElemDesc(Elem), - IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary), - IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), - MoveFn(moveArrayDesc) { + Size(ElemSize * NumElems), MDSize(MD.value_or(0)), + AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize), + ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable), + IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc), + DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { assert(Source && "Missing source"); } Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize) : Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)), - Size(UnknownSizeMark), AllocSize(alignof(void *)), ElemDesc(Elem), - IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true), - CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) { + Size(UnknownSizeMark), MDSize(0), AllocSize(alignof(void *)), + ElemDesc(Elem), IsConst(true), IsMutable(false), IsTemporary(IsTemporary), + IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), + MoveFn(moveArrayDesc) { assert(Source && "Missing source"); } -Descriptor::Descriptor(const DeclTy &D, Record *R, bool IsConst, - bool IsTemporary, bool IsMutable) +Descriptor::Descriptor(const DeclTy &D, Record *R, MetadataSize MD, + bool IsConst, bool IsTemporary, bool IsMutable) : Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())), - Size(ElemSize), AllocSize(Size), ElemRecord(R), IsConst(IsConst), - IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(ctorRecord), - DtorFn(dtorRecord), MoveFn(moveRecord) { + Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize), + ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable), + IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord), + MoveFn(moveRecord) { assert(Source && "Missing source"); } @@ -259,9 +274,7 @@ SourceLocation Descriptor::getLocation() const { } InitMap::InitMap(unsigned N) : UninitFields(N) { - for (unsigned I = 0; I < N / PER_FIELD; ++I) { - data()[I] = 0; - } + std::fill_n(data(), (N + PER_FIELD - 1) / PER_FIELD, 0); } InitMap::T *InitMap::data() { @@ -269,9 +282,14 @@ InitMap::T *InitMap::data() { return reinterpret_cast<T *>(Start); } +const InitMap::T *InitMap::data() const { + auto *Start = reinterpret_cast<const char *>(this) + align(sizeof(InitMap)); + return reinterpret_cast<const T *>(Start); +} + bool InitMap::initialize(unsigned I) { unsigned Bucket = I / PER_FIELD; - unsigned Mask = 1ull << static_cast<uint64_t>(I % PER_FIELD); + T Mask = T(1) << (I % PER_FIELD); if (!(data()[Bucket] & Mask)) { data()[Bucket] |= Mask; UninitFields -= 1; @@ -279,10 +297,9 @@ bool InitMap::initialize(unsigned I) { return UninitFields == 0; } -bool InitMap::isInitialized(unsigned I) { +bool InitMap::isInitialized(unsigned I) const { unsigned Bucket = I / PER_FIELD; - unsigned Mask = 1ull << static_cast<uint64_t>(I % PER_FIELD); - return data()[Bucket] & Mask; + return data()[Bucket] & (T(1) << (I % PER_FIELD)); } InitMap *InitMap::allocate(unsigned N) { diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.h b/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.h index 11072cab3e90..6ef4fc2f4c9b 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.h @@ -47,8 +47,36 @@ using BlockMoveFn = void (*)(Block *Storage, char *SrcFieldPtr, /// Object size as used by the interpreter. using InterpSize = unsigned; +/// Inline descriptor embedded in structures and arrays. +/// +/// Such descriptors precede all composite array elements and structure fields. +/// If the base of a pointer is not zero, the base points to the end of this +/// structure. The offset field is used to traverse the pointer chain up +/// to the root structure which allocated the object. +struct InlineDescriptor { + /// Offset inside the structure/array. + unsigned Offset; + + /// Flag indicating if the storage is constant or not. + /// Relevant for primitive fields. + unsigned IsConst : 1; + /// For primitive fields, it indicates if the field was initialized. + /// Primitive fields in static storage are always initialized. + /// Arrays are always initialized, even though their elements might not be. + /// Base classes are initialized after the constructor is invoked. + unsigned IsInitialized : 1; + /// Flag indicating if the field is an embedded base class. + unsigned IsBase : 1; + /// Flag indicating if the field is the active member of a union. + unsigned IsActive : 1; + /// Flag indicating if the field is mutable (if in a record). + unsigned IsFieldMutable : 1; + + Descriptor *Desc; +}; + /// Describes a memory block created by an allocation site. -struct Descriptor { +struct Descriptor final { private: /// Original declaration, used to emit the error message. const DeclTy Source; @@ -56,6 +84,8 @@ private: const InterpSize ElemSize; /// Size of the storage, in host bytes. const InterpSize Size; + // Size of the metadata. + const InterpSize MDSize; /// Size of the allocation (storage + metadata), in host bytes. const InterpSize AllocSize; @@ -66,6 +96,9 @@ public: /// Token to denote structures of unknown size. struct UnknownSize {}; + using MetadataSize = std::optional<InterpSize>; + static constexpr MetadataSize InlineDescMD = sizeof(InlineDescriptor); + /// Pointer to the record, if block contains records. Record *const ElemRecord = nullptr; /// Descriptor of the array element. @@ -85,26 +118,26 @@ public: const BlockMoveFn MoveFn = nullptr; /// Allocates a descriptor for a primitive. - Descriptor(const DeclTy &D, PrimType Type, bool IsConst, bool IsTemporary, - bool IsMutable); + Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, bool IsConst, + bool IsTemporary, bool IsMutable); /// Allocates a descriptor for an array of primitives. - Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, bool IsConst, - bool IsTemporary, bool IsMutable); + Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, size_t NumElems, + bool IsConst, bool IsTemporary, bool IsMutable); /// Allocates a descriptor for an array of primitives of unknown size. Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize); /// Allocates a descriptor for an array of composites. - Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, bool IsConst, - bool IsTemporary, bool IsMutable); + Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD, + unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable); /// Allocates a descriptor for an array of composites of unknown size. Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize); /// Allocates a descriptor for a record. - Descriptor(const DeclTy &D, Record *R, bool IsConst, bool IsTemporary, - bool IsMutable); + Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst, + bool IsTemporary, bool IsMutable); QualType getType() const; SourceLocation getLocation() const; @@ -113,15 +146,15 @@ public: const Expr *asExpr() const { return Source.dyn_cast<const Expr *>(); } const ValueDecl *asValueDecl() const { - return dyn_cast_or_null<ValueDecl>(asDecl()); + return dyn_cast_if_present<ValueDecl>(asDecl()); } const FieldDecl *asFieldDecl() const { - return dyn_cast_or_null<FieldDecl>(asDecl()); + return dyn_cast_if_present<FieldDecl>(asDecl()); } const RecordDecl *asRecordDecl() const { - return dyn_cast_or_null<RecordDecl>(asDecl()); + return dyn_cast_if_present<RecordDecl>(asDecl()); } /// Returns the size of the object without metadata. @@ -134,6 +167,8 @@ public: unsigned getAllocSize() const { return AllocSize; } /// returns the size of an element when the structure is viewed as an array. unsigned getElemSize() const { return ElemSize; } + /// Returns the size of the metadata. + unsigned getMetadataSize() const { return MDSize; } /// Returns the number of elements stored in the block. unsigned getNumElems() const { @@ -154,39 +189,11 @@ public: bool isArray() const { return IsArray; } }; -/// Inline descriptor embedded in structures and arrays. -/// -/// Such descriptors precede all composite array elements and structure fields. -/// If the base of a pointer is not zero, the base points to the end of this -/// structure. The offset field is used to traverse the pointer chain up -/// to the root structure which allocated the object. -struct InlineDescriptor { - /// Offset inside the structure/array. - unsigned Offset; - - /// Flag indicating if the storage is constant or not. - /// Relevant for primitive fields. - unsigned IsConst : 1; - /// For primitive fields, it indicates if the field was initialized. - /// Primitive fields in static storage are always initialized. - /// Arrays are always initialized, even though their elements might not be. - /// Base classes are initialized after the constructor is invoked. - unsigned IsInitialized : 1; - /// Flag indicating if the field is an embedded base class. - unsigned IsBase : 1; - /// Flag indicating if the field is the active member of a union. - unsigned IsActive : 1; - /// Flag indicating if the field is mutable (if in a record). - unsigned IsMutable : 1; - - Descriptor *Desc; -}; - /// Bitfield tracking the initialisation status of elements of primitive arrays. /// A pointer to this is embedded at the end of all primitive arrays. /// If the map was not yet created and nothing was initialized, the pointer to /// this structure is 0. If the object was fully initialized, the pointer is -1. -struct InitMap { +struct InitMap final { private: /// Type packing bits. using T = uint64_t; @@ -198,13 +205,14 @@ private: /// Returns a pointer to storage. T *data(); + const T *data() const; public: /// Initializes an element. Returns true when object if fully initialized. bool initialize(unsigned I); /// Checks if an element was initialized. - bool isInitialized(unsigned I); + bool isInitialized(unsigned I) const; /// Allocates a map holding N elements. static InitMap *allocate(unsigned N); diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Disasm.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Disasm.cpp index 36adbe296b0c..d31e879d516f 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Disasm.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/Disasm.cpp @@ -21,17 +21,13 @@ using namespace clang; using namespace clang::interp; -template <typename T> -inline std::enable_if_t<!std::is_pointer<T>::value, T> ReadArg(Program &P, - CodePtr OpPC) { - return OpPC.read<T>(); -} - -template <typename T> -inline std::enable_if_t<std::is_pointer<T>::value, T> ReadArg(Program &P, - CodePtr OpPC) { - uint32_t ID = OpPC.read<uint32_t>(); - return reinterpret_cast<T>(P.getNativePointer(ID)); +template <typename T> inline T ReadArg(Program &P, CodePtr &OpPC) { + if constexpr (std::is_pointer_v<T>) { + uint32_t ID = OpPC.read<uint32_t>(); + return reinterpret_cast<T>(P.getNativePointer(ID)); + } else { + return OpPC.read<T>(); + } } LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); } @@ -40,10 +36,11 @@ LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { if (F) { if (auto *Cons = dyn_cast<CXXConstructorDecl>(F)) { DeclarationName Name = Cons->getParent()->getDeclName(); - OS << Name << "::" << Name << ":\n"; + OS << Name << "::" << Name; } else { - OS << F->getDeclName() << ":\n"; + OS << F->getDeclName(); } + OS << " " << (const void*)this << ":\n"; } else { OS << "<<expr>>\n"; } @@ -51,6 +48,7 @@ LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { OS << "frame size: " << getFrameSize() << "\n"; OS << "arg size: " << getArgSize() << "\n"; OS << "rvo: " << hasRVO() << "\n"; + OS << "this arg: " << hasThisPointer() << "\n"; auto PrintName = [&OS](const char *Name) { OS << Name; @@ -74,6 +72,10 @@ LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const { LLVM_DUMP_METHOD void Program::dump() const { dump(llvm::errs()); } LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const { + OS << ":: Program\n"; + OS << "Global Variables: " << Globals.size() << "\n"; + OS << "Functions: " << Funcs.size() << "\n"; + OS << "\n"; for (auto &Func : Funcs) { Func.second->dump(); } diff --git a/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp b/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp index 22e8695b9211..72fd3b45254b 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp @@ -23,7 +23,8 @@ EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk, APValue &Result) : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), Result(Result) { // Create a dummy frame for the interpreter which does not have locals. - S.Current = new InterpFrame(S, nullptr, nullptr, CodePtr(), Pointer()); + S.Current = + new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr()); } llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) { @@ -54,6 +55,16 @@ Scope::Local EvalEmitter::createLocal(Descriptor *D) { auto *B = new (Memory.get()) Block(D, /*isStatic=*/false); B->invokeCtor(); + // Initialize local variable inline descriptor. + InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData()); + Desc.Desc = D; + Desc.Offset = sizeof(InlineDescriptor); + Desc.IsActive = true; + Desc.IsBase = false; + Desc.IsFieldMutable = false; + Desc.IsConst = false; + Desc.IsInitialized = false; + // Register the local. unsigned Off = Locals.size(); Locals.insert({Off, std::move(Memory)}); @@ -123,7 +134,7 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) { const Pointer &FP = Ptr.atField(F.Offset); QualType FieldTy = F.Decl->getType(); if (FP.isActive()) { - if (llvm::Optional<PrimType> T = Ctx.classify(FieldTy)) { + if (std::optional<PrimType> T = Ctx.classify(FieldTy)) { TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value)); } else { Ok &= Composite(FieldTy, FP, Value); @@ -145,7 +156,7 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) { const Pointer &FP = Ptr.atField(FD->Offset); APValue &Value = R.getStructField(I); - if (llvm::Optional<PrimType> T = Ctx.classify(FieldTy)) { + if (std::optional<PrimType> T = Ctx.classify(FieldTy)) { TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value)); } else { Ok &= Composite(FieldTy, FP, Value); @@ -177,7 +188,7 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) { for (unsigned I = 0; I < NumElems; ++I) { APValue &Slot = R.getArrayInitializedElt(I); const Pointer &EP = Ptr.atIndex(I); - if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) { + if (std::optional<PrimType> T = Ctx.classify(ElemTy)) { TYPE_SWITCH(*T, Ok &= ReturnValue<T>(EP.deref<T>(), Slot)); } else { Ok &= Composite(ElemTy, EP.narrow(), Slot); @@ -199,7 +210,8 @@ bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) { auto It = Locals.find(I); assert(It != Locals.end() && "Missing local variable"); - S.Stk.push<Pointer>(reinterpret_cast<Block *>(It->second.get())); + Block *B = reinterpret_cast<Block *>(It->second.get()); + S.Stk.push<Pointer>(B, sizeof(InlineDescriptor)); return true; } @@ -213,7 +225,7 @@ bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) { auto It = Locals.find(I); assert(It != Locals.end() && "Missing local variable"); auto *B = reinterpret_cast<Block *>(It->second.get()); - S.Stk.push<T>(*reinterpret_cast<T *>(B + 1)); + S.Stk.push<T>(*reinterpret_cast<T *>(B->data())); return true; } @@ -227,7 +239,10 @@ bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) { auto It = Locals.find(I); assert(It != Locals.end() && "Missing local variable"); auto *B = reinterpret_cast<Block *>(It->second.get()); - *reinterpret_cast<T *>(B + 1) = S.Stk.pop<T>(); + *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>(); + InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData()); + Desc.IsInitialized = true; + return true; } diff --git a/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.h b/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.h index eec2ff8ee753..6b6d0d621901 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.h @@ -23,7 +23,6 @@ #include "llvm/Support/Error.h" namespace clang { -class FunctionDecl; namespace interp { class Context; class Function; @@ -71,7 +70,7 @@ protected: Local createLocal(Descriptor *D); /// Returns the source location of the current opcode. - SourceInfo getSource(Function *F, CodePtr PC) const override { + SourceInfo getSource(const Function *F, CodePtr PC) const override { return F ? F->getSource(PC) : CurrentSource; } @@ -97,7 +96,7 @@ private: // value which is mapped to the location of the opcode being evaluated. CodePtr OpPC; /// Location of a failure. - llvm::Optional<SourceLocation> BailLocation; + std::optional<SourceLocation> BailLocation; /// Location of the current instruction. SourceInfo CurrentSource; @@ -110,12 +109,7 @@ private: /// Since expressions can only jump forward, predicated execution is /// used to deal with if-else statements. - bool isActive() { return CurrentLabel == ActiveLabel; } - - /// Helper to invoke a method. - bool ExecuteCall(Function *F, Pointer &&This, const SourceInfo &Info); - /// Helper to emit a diagnostic on a missing method. - bool ExecuteNoCall(const FunctionDecl *F, const SourceInfo &Info); + bool isActive() const { return CurrentLabel == ActiveLabel; } protected: #define GET_EVAL_PROTO diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Function.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Function.cpp index 6ba97df1cd30..40001faad411 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Function.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/Function.cpp @@ -17,13 +17,11 @@ using namespace clang::interp; Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize, llvm::SmallVector<PrimType, 8> &&ParamTypes, - llvm::DenseMap<unsigned, ParamDescriptor> &&Params) + llvm::DenseMap<unsigned, ParamDescriptor> &&Params, + bool HasThisPointer, bool HasRVO) : P(P), Loc(F->getBeginLoc()), F(F), ArgSize(ArgSize), - ParamTypes(std::move(ParamTypes)), Params(std::move(Params)) {} - -CodePtr Function::getCodeBegin() const { return Code.data(); } - -CodePtr Function::getCodeEnd() const { return Code.data() + Code.size(); } + ParamTypes(std::move(ParamTypes)), Params(std::move(Params)), + HasThisPointer(HasThisPointer), HasRVO(HasRVO) {} Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const { auto It = Params.find(Offset); @@ -32,11 +30,12 @@ Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const { } SourceInfo Function::getSource(CodePtr PC) const { + assert(PC >= getCodeBegin() && "PC does not belong to this function"); + assert(PC <= getCodeEnd() && "PC Does not belong to this function"); unsigned Offset = PC - getCodeBegin(); using Elem = std::pair<unsigned, SourceInfo>; auto It = llvm::lower_bound(SrcMap, Elem{Offset, {}}, llvm::less_first()); - if (It == SrcMap.end() || It->first != Offset) - llvm::report_fatal_error("missing source location"); + assert(It != SrcMap.end()); return It->second; } diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Function.h b/contrib/llvm-project/clang/lib/AST/Interp/Function.h index ac1dffea1160..5b2a77f1a12d 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Function.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/Function.h @@ -29,7 +29,7 @@ enum PrimType : uint32_t; /// Describes a scope block. /// /// The block gathers all the descriptors of the locals defined in this block. -class Scope { +class Scope final { public: /// Information about a local's storage. struct Local { @@ -43,7 +43,7 @@ public: Scope(LocalVectorTy &&Descriptors) : Descriptors(std::move(Descriptors)) {} - llvm::iterator_range<LocalVectorTy::iterator> locals() { + llvm::iterator_range<LocalVectorTy::const_iterator> locals() const { return llvm::make_range(Descriptors.begin(), Descriptors.end()); } @@ -56,23 +56,42 @@ private: /// /// Contains links to the bytecode of the function, as well as metadata /// describing all arguments and stack-local variables. -class Function { +/// +/// # Calling Convention +/// +/// When calling a function, all argument values must be on the stack. +/// +/// If the function has a This pointer (i.e. hasThisPointer() returns true, +/// the argument values need to be preceeded by a Pointer for the This object. +/// +/// If the function uses Return Value Optimization, the arguments (and +/// potentially the This pointer) need to be proceeded by a Pointer pointing +/// to the location to construct the returned value. +/// +/// After the function has been called, it will remove all arguments, +/// including RVO and This pointer, from the stack. +/// +class Function final { public: using ParamDescriptor = std::pair<PrimType, Descriptor *>; /// Returns the size of the function's local stack. unsigned getFrameSize() const { return FrameSize; } - /// Returns the size of the argument stackx + /// Returns the size of the argument stack. unsigned getArgSize() const { return ArgSize; } /// Returns a pointer to the start of the code. - CodePtr getCodeBegin() const; + CodePtr getCodeBegin() const { return Code.data(); } /// Returns a pointer to the end of the code. - CodePtr getCodeEnd() const; + CodePtr getCodeEnd() const { return Code.data() + Code.size(); } /// Returns the original FunctionDecl. const FunctionDecl *getDecl() const { return F; } + /// Returns the name of the function decl this code + /// was generated for. + const std::string getName() const { return F->getNameInfo().getAsString(); } + /// Returns the location. SourceLocation getLoc() const { return Loc; } @@ -80,21 +99,24 @@ public: ParamDescriptor getParamDescriptor(unsigned Offset) const; /// Checks if the first argument is a RVO pointer. - bool hasRVO() const { return ParamTypes.size() != Params.size(); } + bool hasRVO() const { return HasRVO; } /// Range over the scope blocks. - llvm::iterator_range<llvm::SmallVector<Scope, 2>::iterator> scopes() { + llvm::iterator_range<llvm::SmallVector<Scope, 2>::const_iterator> + scopes() const { return llvm::make_range(Scopes.begin(), Scopes.end()); } /// Range over argument types. - using arg_reverse_iterator = SmallVectorImpl<PrimType>::reverse_iterator; - llvm::iterator_range<arg_reverse_iterator> args_reverse() { - return llvm::make_range(ParamTypes.rbegin(), ParamTypes.rend()); + using arg_reverse_iterator = + SmallVectorImpl<PrimType>::const_reverse_iterator; + llvm::iterator_range<arg_reverse_iterator> args_reverse() const { + return llvm::reverse(ParamTypes); } /// Returns a specific scope. Scope &getScope(unsigned Idx) { return Scopes[Idx]; } + const Scope &getScope(unsigned Idx) const { return Scopes[Idx]; } /// Returns the source information at a given PC. SourceInfo getSource(CodePtr PC) const; @@ -108,11 +130,22 @@ public: /// Checks if the function is a constructor. bool isConstructor() const { return isa<CXXConstructorDecl>(F); } + /// Checks if the function is fully done compiling. + bool isFullyCompiled() const { return IsFullyCompiled; } + + bool hasThisPointer() const { return HasThisPointer; } + + // Checks if the funtion already has a body attached. + bool hasBody() const { return HasBody; } + + unsigned getNumParams() const { return ParamTypes.size(); } + private: /// Construct a function representing an actual function. Function(Program &P, const FunctionDecl *F, unsigned ArgSize, llvm::SmallVector<PrimType, 8> &&ParamTypes, - llvm::DenseMap<unsigned, ParamDescriptor> &&Params); + llvm::DenseMap<unsigned, ParamDescriptor> &&Params, + bool HasThisPointer, bool HasRVO); /// Sets the code of a function. void setCode(unsigned NewFrameSize, std::vector<char> &&NewCode, SourceMap &&NewSrcMap, @@ -122,8 +155,11 @@ private: SrcMap = std::move(NewSrcMap); Scopes = std::move(NewScopes); IsValid = true; + HasBody = true; } + void setIsFullyCompiled(bool FC) { IsFullyCompiled = FC; } + private: friend class Program; friend class ByteCodeEmitter; @@ -135,7 +171,7 @@ private: /// Declaration this function was compiled from. const FunctionDecl *F; /// Local area size: storage + metadata. - unsigned FrameSize; + unsigned FrameSize = 0; /// Size of the argument stack. unsigned ArgSize; /// Program code. @@ -150,6 +186,18 @@ private: llvm::DenseMap<unsigned, ParamDescriptor> Params; /// Flag to indicate if the function is valid. bool IsValid = false; + /// Flag to indicate if the function is done being + /// compiled to bytecode. + bool IsFullyCompiled = false; + /// Flag indicating if this function takes the this pointer + /// as the first implicit argument + bool HasThisPointer = false; + /// Whether this function has Return Value Optimization, i.e. + /// the return value is constructed in the caller's stack frame. + /// This is done for functions that return non-primive values. + bool HasRVO = false; + /// If we've already compiled the function's body. + bool HasBody = false; public: /// Dumps the disassembled bytecode to \c llvm::errs(). diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Integral.h b/contrib/llvm-project/clang/lib/AST/Interp/Integral.h index 46cd611ee389..8a742333ae57 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Integral.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/Integral.h @@ -53,17 +53,17 @@ template <> struct Repr<64, true> { using Type = int64_t; }; /// These wrappers are required to shared an interface between APSint and /// builtin primitive numeral types, while optimising for storage and /// allowing methods operating on primitive type to compile to fast code. -template <unsigned Bits, bool Signed> class Integral { +template <unsigned Bits, bool Signed> class Integral final { private: template <unsigned OtherBits, bool OtherSigned> friend class Integral; // The primitive representing the integral. - using T = typename Repr<Bits, Signed>::Type; - T V; + using ReprT = typename Repr<Bits, Signed>::Type; + ReprT V; /// Primitive representing limits. - static const auto Min = std::numeric_limits<T>::min(); - static const auto Max = std::numeric_limits<T>::max(); + static const auto Min = std::numeric_limits<ReprT>::min(); + static const auto Max = std::numeric_limits<ReprT>::max(); /// Construct an integral from anything that is convertible to storage. template <typename T> explicit Integral(T V) : V(V) {} @@ -107,7 +107,7 @@ public: return APSInt(APInt(Bits, static_cast<uint64_t>(V), Signed), !Signed); } APSInt toAPSInt(unsigned NumBits) const { - if (Signed) + if constexpr (Signed) return APSInt(toAPSInt().sextOrTrunc(NumBits), !Signed); else return APSInt(toAPSInt().zextOrTrunc(NumBits), !Signed); @@ -124,25 +124,27 @@ public: bool isMin() const { return *this == min(bitWidth()); } - bool isMinusOne() const { return Signed && V == T(-1); } + bool isMinusOne() const { return Signed && V == ReprT(-1); } constexpr static bool isSigned() { return Signed; } - bool isNegative() const { return V < T(0); } + bool isNegative() const { return V < ReprT(0); } bool isPositive() const { return !isNegative(); } ComparisonCategoryResult compare(const Integral &RHS) const { return Compare(V, RHS.V); } - unsigned countLeadingZeros() const { return llvm::countLeadingZeros<T>(V); } + unsigned countLeadingZeros() const { + return llvm::countLeadingZeros<ReprT>(V); + } Integral truncate(unsigned TruncBits) const { if (TruncBits >= Bits) return *this; - const T BitMask = (T(1) << T(TruncBits)) - 1; - const T SignBit = T(1) << (TruncBits - 1); - const T ExtMask = ~BitMask; + const ReprT BitMask = (ReprT(1) << ReprT(TruncBits)) - 1; + const ReprT SignBit = ReprT(1) << (TruncBits - 1); + const ReprT ExtMask = ~BitMask; return Integral((V & BitMask) | (Signed && (V & SignBit) ? ExtMask : 0)); } @@ -155,9 +157,11 @@ public: return Integral(Max); } - template <typename T> - static std::enable_if_t<std::is_integral<T>::value, Integral> from(T Value) { - return Integral(Value); + template <typename ValT> static Integral from(ValT Value) { + if constexpr (std::is_integral<ValT>::value) + return Integral(Value); + else + return Integral::from(static_cast<Integral::ReprT>(Value)); } template <unsigned SrcBits, bool SrcSign> @@ -167,7 +171,7 @@ public: } template <bool SrcSign> static Integral from(Integral<0, SrcSign> Value) { - if (SrcSign) + if constexpr (SrcSign) return Integral(Value.V.getSExtValue()); else return Integral(Value.V.getZExtValue()); @@ -180,15 +184,15 @@ public: } static bool inRange(int64_t Value, unsigned NumBits) { - return CheckRange<T, Min, Max>(Value); + return CheckRange<ReprT, Min, Max>(Value); } static bool increment(Integral A, Integral *R) { - return add(A, Integral(T(1)), A.bitWidth(), R); + return add(A, Integral(ReprT(1)), A.bitWidth(), R); } static bool decrement(Integral A, Integral *R) { - return sub(A, Integral(T(1)), A.bitWidth(), R); + return sub(A, Integral(ReprT(1)), A.bitWidth(), R); } static bool add(Integral A, Integral B, unsigned OpBits, Integral *R) { @@ -203,56 +207,74 @@ public: return CheckMulUB(A.V, B.V, R->V); } -private: - template <typename T> - static std::enable_if_t<std::is_signed<T>::value, bool> CheckAddUB(T A, T B, - T &R) { - return llvm::AddOverflow<T>(A, B, R); + static bool rem(Integral A, Integral B, unsigned OpBits, Integral *R) { + *R = Integral(A.V % B.V); + return false; + } + + static bool div(Integral A, Integral B, unsigned OpBits, Integral *R) { + *R = Integral(A.V / B.V); + return false; } - template <typename T> - static std::enable_if_t<std::is_unsigned<T>::value, bool> CheckAddUB(T A, T B, - T &R) { - R = A + B; + static bool bitAnd(Integral A, Integral B, unsigned OpBits, Integral *R) { + *R = Integral(A.V & B.V); return false; } - template <typename T> - static std::enable_if_t<std::is_signed<T>::value, bool> CheckSubUB(T A, T B, - T &R) { - return llvm::SubOverflow<T>(A, B, R); + static bool bitOr(Integral A, Integral B, unsigned OpBits, Integral *R) { + *R = Integral(A.V | B.V); + return false; } - template <typename T> - static std::enable_if_t<std::is_unsigned<T>::value, bool> CheckSubUB(T A, T B, - T &R) { - R = A - B; + static bool bitXor(Integral A, Integral B, unsigned OpBits, Integral *R) { + *R = Integral(A.V ^ B.V); return false; } - template <typename T> - static std::enable_if_t<std::is_signed<T>::value, bool> CheckMulUB(T A, T B, - T &R) { - return llvm::MulOverflow<T>(A, B, R); + static bool neg(Integral A, Integral *R) { + *R = -A; + return false; } - template <typename T> - static std::enable_if_t<std::is_unsigned<T>::value, bool> CheckMulUB(T A, T B, - T &R) { - R = A * B; + static bool comp(Integral A, Integral *R) { + *R = Integral(~A.V); return false; } - template <typename T, T Min, T Max> - static std::enable_if_t<std::is_signed<T>::value, bool> - CheckRange(int64_t V) { - return Min <= V && V <= Max; +private: + template <typename T> static bool CheckAddUB(T A, T B, T &R) { + if constexpr (std::is_signed_v<T>) { + return llvm::AddOverflow<T>(A, B, R); + } else { + R = A + B; + return false; + } } - template <typename T, T Min, T Max> - static std::enable_if_t<std::is_unsigned<T>::value, bool> - CheckRange(int64_t V) { - return V >= 0 && static_cast<uint64_t>(V) <= Max; + template <typename T> static bool CheckSubUB(T A, T B, T &R) { + if constexpr (std::is_signed_v<T>) { + return llvm::SubOverflow<T>(A, B, R); + } else { + R = A - B; + return false; + } + } + + 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 { + R = A * B; + return false; + } + } + template <typename T, T Min, T Max> static bool CheckRange(int64_t V) { + if constexpr (std::is_signed_v<T>) { + return Min <= V && V <= Max; + } else { + return V >= 0 && static_cast<uint64_t>(V) <= Max; + } } }; diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp index cec3f6d6160e..6a600b306bad 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp @@ -1,4 +1,4 @@ -//===--- InterpState.cpp - Interpreter for the constexpr VM -----*- C++ -*-===// +//===------- Interp.cpp - Interpreter for the constexpr VM ------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -201,8 +201,8 @@ bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK) { - const auto &Src = S.Current->getSource(OpPC); if (Ptr.isZero()) { + const auto &Src = S.Current->getSource(OpPC); if (Ptr.isField()) S.FFDiag(Src, diag::note_constexpr_null_subobject) << CSK_Field; @@ -213,6 +213,7 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, } if (!Ptr.isLive()) { + const auto &Src = S.Current->getSource(OpPC); bool IsTemp = Ptr.isTemporary(); S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp; @@ -330,17 +331,18 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { return true; } -bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F) { - const SourceLocation &Loc = S.Current->getLocation(OpPC); +bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { if (F->isVirtual()) { if (!S.getLangOpts().CPlusPlus20) { + const SourceLocation &Loc = S.Current->getLocation(OpPC); S.CCEDiag(Loc, diag::note_constexpr_virtual_call); return false; } } if (!F->isConstexpr()) { + const SourceLocation &Loc = S.Current->getLocation(OpPC); if (S.getLangOpts().CPlusPlus11) { const FunctionDecl *DiagDecl = F->getDecl(); @@ -398,9 +400,92 @@ bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) { S.Note(MD->getLocation(), diag::note_declared_at); return false; } + +static void DiagnoseUninitializedSubobject(InterpState &S, const SourceInfo &SI, + QualType SubObjType, + SourceLocation SubObjLoc) { + S.FFDiag(SI, diag::note_constexpr_uninitialized) << true << SubObjType; + if (SubObjLoc.isValid()) + S.Note(SubObjLoc, diag::note_constexpr_subobject_declared_here); +} + +static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC, + const Pointer &BasePtr, const Record *R); + +static bool CheckArrayInitialized(InterpState &S, CodePtr OpPC, + const Pointer &BasePtr, + const ConstantArrayType *CAT) { + bool Result = true; + size_t NumElems = CAT->getSize().getZExtValue(); + QualType ElemType = CAT->getElementType(); + + if (isa<RecordType>(ElemType.getTypePtr())) { + const Record *R = BasePtr.getElemRecord(); + for (size_t I = 0; I != NumElems; ++I) { + Pointer ElemPtr = BasePtr.atIndex(I).narrow(); + Result &= CheckFieldsInitialized(S, OpPC, ElemPtr, R); + } + } else if (auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) { + for (size_t I = 0; I != NumElems; ++I) { + Pointer ElemPtr = BasePtr.atIndex(I).narrow(); + Result &= CheckArrayInitialized(S, OpPC, ElemPtr, ElemCAT); + } + } else { + for (size_t I = 0; I != NumElems; ++I) { + if (!BasePtr.atIndex(I).isInitialized()) { + DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), ElemType, + BasePtr.getFieldDesc()->getLocation()); + Result = false; + } + } + } + + return Result; +} + +static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC, + const Pointer &BasePtr, const Record *R) { + assert(R); + bool Result = true; + // Check all fields of this record are initialized. + for (const Record::Field &F : R->fields()) { + Pointer FieldPtr = BasePtr.atField(F.Offset); + QualType FieldType = F.Decl->getType(); + + if (FieldType->isRecordType()) { + Result &= CheckFieldsInitialized(S, OpPC, FieldPtr, FieldPtr.getRecord()); + } else if (FieldType->isArrayType()) { + const auto *CAT = + cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe()); + Result &= CheckArrayInitialized(S, OpPC, FieldPtr, CAT); + } else if (!FieldPtr.isInitialized()) { + DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), + F.Decl->getType(), F.Decl->getLocation()); + Result = false; + } + } + return Result; +} + +bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This) { + assert(!This.isZero()); + const Record *R = This.getRecord(); + return CheckFieldsInitialized(S, OpPC, This, R); +} + bool Interpret(InterpState &S, APValue &Result) { + // The current stack frame when we started Interpret(). + // This is being used by the ops to determine wheter + // to return from this function and thus terminate + // interpretation. + const InterpFrame *StartFrame = S.Current; + assert(!S.Current->isRoot()); CodePtr PC = S.Current->getPC(); + // Empty program. + if (!PC) + return true; + for (;;) { auto Op = PC.read<Opcode>(); CodePtr OpPC = PC; diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Interp.h b/contrib/llvm-project/clang/lib/AST/Interp/Interp.h index a1d90f26ba46..ed3accd98a90 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Interp.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/Interp.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_AST_INTERP_INTERP_H #define LLVM_CLANG_AST_INTERP_INTERP_H +#include "Boolean.h" #include "Function.h" #include "InterpFrame.h" #include "InterpStack.h" @@ -30,7 +31,6 @@ #include "llvm/Support/Endian.h" #include <limits> #include <type_traits> -#include <vector> namespace clang { namespace interp { @@ -84,7 +84,7 @@ bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr); bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if a method can be called. -bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F); +bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F); /// Checks the 'this' pointer. bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This); @@ -92,7 +92,53 @@ bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This); /// Checks if a method is pure virtual. bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD); -template <typename T> inline bool IsTrue(const T &V) { return !V.isZero(); } +/// Checks that all fields are initialized after a constructor call. +bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This); + +/// Checks if the shift operation is legal. +template <typename RT> +bool CheckShift(InterpState &S, CodePtr OpPC, const RT &RHS, unsigned Bits) { + if (RHS.isNegative()) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); + return false; + } + + // C++11 [expr.shift]p1: Shift width must be less than the bit width of + // the shifted type. + if (Bits > 1 && RHS >= RT::from(Bits, RHS.bitWidth())) { + const Expr *E = S.Current->getExpr(OpPC); + const APSInt Val = RHS.toAPSInt(); + QualType Ty = E->getType(); + S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits; + return false; + } + return true; +} + +/// Checks if Div/Rem operation on LHS and RHS is valid. +template <typename T> +bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) { + if (RHS.isZero()) { + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_expr_divide_by_zero); + return false; + } + + if (LHS.isSigned() && LHS.isMin() && RHS.isNegative() && RHS.isMinusOne()) { + APSInt LHSInt = LHS.toAPSInt(); + SmallString<32> Trunc; + (-LHSInt.extend(LHSInt.getBitWidth() + 1)).toString(Trunc, 10); + const SourceInfo &Loc = S.Current->getSource(OpPC); + const Expr *E = S.Current->getExpr(OpPC); + S.CCEDiag(Loc, diag::note_constexpr_overflow) << Trunc << E->getType(); + return false; + } + return true; +} + +/// Interpreter entry point. +bool Interpret(InterpState &S, APValue &Result); //===----------------------------------------------------------------------===// // Add, Sub, Mul @@ -154,6 +200,240 @@ bool Mul(InterpState &S, CodePtr OpPC) { return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS); } +/// 1) Pops the RHS from the stack. +/// 2) Pops the LHS from the stack. +/// 3) Pushes 'LHS & RHS' on the stack +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool BitAnd(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop<T>(); + const T &LHS = S.Stk.pop<T>(); + + unsigned Bits = RHS.bitWidth(); + T Result; + if (!T::bitAnd(LHS, RHS, Bits, &Result)) { + S.Stk.push<T>(Result); + return true; + } + return false; +} + +/// 1) Pops the RHS from the stack. +/// 2) Pops the LHS from the stack. +/// 3) Pushes 'LHS | RHS' on the stack +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool BitOr(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop<T>(); + const T &LHS = S.Stk.pop<T>(); + + unsigned Bits = RHS.bitWidth(); + T Result; + if (!T::bitOr(LHS, RHS, Bits, &Result)) { + S.Stk.push<T>(Result); + return true; + } + return false; +} + +/// 1) Pops the RHS from the stack. +/// 2) Pops the LHS from the stack. +/// 3) Pushes 'LHS ^ RHS' on the stack +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool BitXor(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop<T>(); + const T &LHS = S.Stk.pop<T>(); + + unsigned Bits = RHS.bitWidth(); + T Result; + if (!T::bitXor(LHS, RHS, Bits, &Result)) { + S.Stk.push<T>(Result); + return true; + } + return false; +} + +/// 1) Pops the RHS from the stack. +/// 2) Pops the LHS from the stack. +/// 3) Pushes 'LHS % RHS' on the stack (the remainder of dividing LHS by RHS). +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Rem(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop<T>(); + const T &LHS = S.Stk.pop<T>(); + + if (!CheckDivRem(S, OpPC, LHS, RHS)) + return false; + + const unsigned Bits = RHS.bitWidth() * 2; + T Result; + if (!T::rem(LHS, RHS, Bits, &Result)) { + S.Stk.push<T>(Result); + return true; + } + return false; +} + +/// 1) Pops the RHS from the stack. +/// 2) Pops the LHS from the stack. +/// 3) Pushes 'LHS / RHS' on the stack +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Div(InterpState &S, CodePtr OpPC) { + const T &RHS = S.Stk.pop<T>(); + const T &LHS = S.Stk.pop<T>(); + + if (!CheckDivRem(S, OpPC, LHS, RHS)) + return false; + + const unsigned Bits = RHS.bitWidth() * 2; + T Result; + if (!T::div(LHS, RHS, Bits, &Result)) { + S.Stk.push<T>(Result); + return true; + } + return false; +} + +//===----------------------------------------------------------------------===// +// Inv +//===----------------------------------------------------------------------===// + +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Inv(InterpState &S, CodePtr OpPC) { + using BoolT = PrimConv<PT_Bool>::T; + const T &Val = S.Stk.pop<T>(); + const unsigned Bits = Val.bitWidth(); + Boolean R; + Boolean::inv(BoolT::from(Val, Bits), &R); + + S.Stk.push<BoolT>(R); + return true; +} + +//===----------------------------------------------------------------------===// +// Neg +//===----------------------------------------------------------------------===// + +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Neg(InterpState &S, CodePtr OpPC) { + const T &Val = S.Stk.pop<T>(); + T Result; + T::neg(Val, &Result); + + S.Stk.push<T>(Result); + return true; +} + +enum class PushVal : bool { + No, + Yes, +}; +enum class IncDecOp { + Inc, + Dec, +}; + +template <typename T, IncDecOp Op, PushVal DoPush> +bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { + T Value = Ptr.deref<T>(); + T Result; + + if constexpr (DoPush == PushVal::Yes) + S.Stk.push<T>(Result); + + if constexpr (Op == IncDecOp::Inc) { + if (!T::increment(Value, &Result)) { + Ptr.deref<T>() = Result; + return true; + } + } else { + if (!T::decrement(Value, &Result)) { + Ptr.deref<T>() = Result; + return true; + } + } + + // Something went wrong with the previous operation. Compute the + // result with another bit of precision. + unsigned Bits = Value.bitWidth() + 1; + APSInt APResult; + if constexpr (Op == IncDecOp::Inc) + APResult = ++Value.toAPSInt(Bits); + else + APResult = --Value.toAPSInt(Bits); + + // Report undefined behaviour, stopping if required. + const Expr *E = S.Current->getExpr(OpPC); + QualType Type = E->getType(); + if (S.checkingForUndefinedBehavior()) { + SmallString<32> Trunc; + APResult.trunc(Result.bitWidth()).toString(Trunc, 10); + auto Loc = E->getExprLoc(); + S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type; + return true; + } + + S.CCEDiag(E, diag::note_constexpr_overflow) << APResult << Type; + return S.noteUndefinedBehavior(); +} + +/// 1) Pops a pointer from the stack +/// 2) Load the value from the pointer +/// 3) Writes the value increased by one back to the pointer +/// 4) Pushes the original (pre-inc) value on the stack. +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Inc(InterpState &S, CodePtr OpPC) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop<Pointer>(); + + return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr); +} + +/// 1) Pops a pointer from the stack +/// 2) Load the value from the pointer +/// 3) Writes the value increased by one back to the pointer +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool IncPop(InterpState &S, CodePtr OpPC) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop<Pointer>(); + + return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr); +} + +/// 1) Pops a pointer from the stack +/// 2) Load the value from the pointer +/// 3) Writes the value decreased by one back to the pointer +/// 4) Pushes the original (pre-dec) value on the stack. +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Dec(InterpState &S, CodePtr OpPC) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop<Pointer>(); + + return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr); +} + +/// 1) Pops a pointer from the stack +/// 2) Load the value from the pointer +/// 3) Writes the value decreased by one back to the pointer +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool DecPop(InterpState &S, CodePtr OpPC) { + // FIXME: Check initialization of Ptr + const Pointer &Ptr = S.Stk.pop<Pointer>(); + + return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr); +} + +/// 1) Pops the value from the stack. +/// 2) Pushes the bitwise complemented value on the stack (~V). +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool Comp(InterpState &S, CodePtr OpPC) { + const T &Val = S.Stk.pop<T>(); + T Result; + if (!T::comp(Val, &Result)) { + S.Stk.push<T>(Result); + return true; + } + + return false; +} + //===----------------------------------------------------------------------===// // EQ, NE, GT, GE, LT, LE //===----------------------------------------------------------------------===// @@ -209,6 +489,16 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) { } else { unsigned VL = LHS.getByteOffset(); unsigned VR = RHS.getByteOffset(); + + // In our Pointer class, a pointer to an array and a pointer to the first + // element in the same array are NOT equal. They have the same Base value, + // but a different Offset. This is a pretty rare case, so we fix this here + // by comparing pointers to the first elements. + if (LHS.inArray() && LHS.isRoot()) + VL = LHS.atIndex(0).getByteOffset(); + if (RHS.inArray() && RHS.isRoot()) + VR = RHS.atIndex(0).getByteOffset(); + S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR)))); return true; } @@ -304,7 +594,10 @@ bool Const(InterpState &S, CodePtr OpPC, const T &Arg) { template <PrimType Name, class T = typename PrimConv<Name>::T> bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { - S.Stk.push<T>(S.Current->getLocal<T>(I)); + const Pointer &Ptr = S.Current->getLocalPointer(I); + if (!CheckLoad(S, OpPC, Ptr)) + return false; + S.Stk.push<T>(Ptr.deref<T>()); return true; } @@ -329,6 +622,8 @@ bool SetParam(InterpState &S, CodePtr OpPC, uint32_t I) { return true; } +/// 1) Peeks a pointer on the stack +/// 2) Pushes the value of the pointer's field on the stack template <PrimType Name, class T = typename PrimConv<Name>::T> bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) { const Pointer &Obj = S.Stk.peek<Pointer>(); @@ -358,6 +653,8 @@ bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) { return true; } +/// 1) Pops a pointer from the stack +/// 2) Pushes the value of the pointer's field on the stack template <PrimType Name, class T = typename PrimConv<Name>::T> bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) { const Pointer &Obj = S.Stk.pop<Pointer>(); @@ -463,10 +760,13 @@ bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) { return true; } +/// 1) Pops the value from the stack +/// 2) Peeks a pointer from the stack +/// 3) Pushes the value to field I of the pointer on the stack template <PrimType Name, class T = typename PrimConv<Name>::T> bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) { const T &Value = S.Stk.pop<T>(); - const Pointer &Field = S.Stk.pop<Pointer>().atField(I); + const Pointer &Field = S.Stk.peek<Pointer>().atField(I); Field.deref<T>() = Value; Field.activate(); Field.initialize(); @@ -516,6 +816,8 @@ inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { return true; } +/// 1) Pops a Pointer from the stack +/// 2) Pushes Pointer.atField(Off) on the stack inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (!CheckNull(S, OpPC, Ptr, CSK_Field)) @@ -638,6 +940,8 @@ bool Store(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.peek<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; + if (!Ptr.isRoot()) + Ptr.initialize(); Ptr.deref<T>() = Value; return true; } @@ -648,6 +952,8 @@ bool StorePop(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; + if (!Ptr.isRoot()) + Ptr.initialize(); Ptr.deref<T>() = Value; return true; } @@ -658,6 +964,8 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.peek<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; + if (!Ptr.isRoot()) + Ptr.initialize(); if (auto *FD = Ptr.getField()) { Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx())); } else { @@ -672,6 +980,8 @@ bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; + if (!Ptr.isRoot()) + Ptr.initialize(); if (auto *FD = Ptr.getField()) { Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx())); } else { @@ -691,6 +1001,9 @@ bool InitPop(InterpState &S, CodePtr OpPC) { return true; } +/// 1) Pops the value from the stack +/// 2) Peeks a pointer and gets its index \Idx +/// 3) Sets the value on the pointer, leaving the pointer on the stack. template <PrimType Name, class T = typename PrimConv<Name>::T> bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) { const T &Value = S.Stk.pop<T>(); @@ -702,6 +1015,7 @@ bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) { return true; } +/// The same as InitElem, but pops the pointer as well. template <PrimType Name, class T = typename PrimConv<Name>::T> bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) { const T &Value = S.Stk.pop<T>(); @@ -721,23 +1035,25 @@ template <class T, bool Add> bool OffsetHelper(InterpState &S, CodePtr OpPC) { // Fetch the pointer and the offset. const T &Offset = S.Stk.pop<T>(); const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex)) - return false; + if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer)) return false; - // Get a version of the index comparable to the type. - T Index = T::from(Ptr.getIndex(), Offset.bitWidth()); - // A zero offset does not change the pointer, but in the case of an array - // it has to be adjusted to point to the first element instead of the array. + // A zero offset does not change the pointer. if (Offset.isZero()) { - S.Stk.push<Pointer>(Index.isZero() ? Ptr.atIndex(0) : Ptr); + S.Stk.push<Pointer>(Ptr); return true; } + + if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex)) + return false; + // Arrays of unknown bounds cannot have pointers into them. if (!CheckArray(S, OpPC, Ptr)) return false; + // Get a version of the index comparable to the type. + T Index = T::from(Ptr.getIndex(), Offset.bitWidth()); // Compute the largest index into the array. unsigned MaxIndex = Ptr.getNumElems(); @@ -754,23 +1070,34 @@ template <class T, bool Add> bool OffsetHelper(InterpState &S, CodePtr OpPC) { return false; }; - // If the new offset would be negative, bail out. - if (Add && Offset.isNegative() && (Offset.isMin() || -Offset > Index)) - return InvalidOffset(); - if (!Add && Offset.isPositive() && Index < Offset) - return InvalidOffset(); - - // If the new offset would be out of bounds, bail out. unsigned MaxOffset = MaxIndex - Ptr.getIndex(); - if (Add && Offset.isPositive() && Offset > MaxOffset) - return InvalidOffset(); - if (!Add && Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset)) - return InvalidOffset(); + if constexpr (Add) { + // If the new offset would be negative, bail out. + if (Offset.isNegative() && (Offset.isMin() || -Offset > Index)) + return InvalidOffset(); + + // If the new offset would be out of bounds, bail out. + if (Offset.isPositive() && Offset > MaxOffset) + return InvalidOffset(); + } else { + // If the new offset would be negative, bail out. + if (Offset.isPositive() && Index < Offset) + return InvalidOffset(); + + // If the new offset would be out of bounds, bail out. + if (Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset)) + return InvalidOffset(); + } // Offset is valid - compute it on unsigned. int64_t WideIndex = static_cast<int64_t>(Index); int64_t WideOffset = static_cast<int64_t>(Offset); - int64_t Result = Add ? (WideIndex + WideOffset) : (WideIndex - WideOffset); + int64_t Result; + if constexpr (Add) + Result = WideIndex + WideOffset; + else + Result = WideIndex - WideOffset; + S.Stk.push<Pointer>(Ptr.atIndex(static_cast<unsigned>(Result))); return true; } @@ -785,6 +1112,23 @@ bool SubOffset(InterpState &S, CodePtr OpPC) { return OffsetHelper<T, false>(S, OpPC); } +/// 1) Pops a Pointer from the stack. +/// 2) Pops another Pointer from the stack. +/// 3) Pushes the different of the indices of the two pointers on the stack. +template <PrimType Name, class T = typename PrimConv<Name>::T> +inline bool SubPtr(InterpState &S, CodePtr OpPC) { + const Pointer &LHS = S.Stk.pop<Pointer>(); + const Pointer &RHS = S.Stk.pop<Pointer>(); + + if (!Pointer::hasSameArray(LHS, RHS)) { + // TODO: Diagnose. + return false; + } + + T A = T::from(LHS.getIndex()); + T B = T::from(RHS.getIndex()); + return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, A.bitWidth(), A, B); +} //===----------------------------------------------------------------------===// // Destroy @@ -840,88 +1184,47 @@ inline bool This(InterpState &S, CodePtr OpPC) { return true; } +inline bool RVOPtr(InterpState &S, CodePtr OpPC) { + assert(S.Current->getFunction()->hasRVO()); + S.Stk.push<Pointer>(S.Current->getRVOPtr()); + return true; +} + //===----------------------------------------------------------------------===// // Shr, Shl //===----------------------------------------------------------------------===// -template <PrimType TR, PrimType TL, class T = typename PrimConv<TR>::T> -unsigned Trunc(InterpState &S, CodePtr OpPC, unsigned Bits, const T &V) { - // C++11 [expr.shift]p1: Shift width must be less than the bit width of - // the shifted type. - if (Bits > 1 && V >= T::from(Bits, V.bitWidth())) { - const Expr *E = S.Current->getExpr(OpPC); - const APSInt Val = V.toAPSInt(); - QualType Ty = E->getType(); - S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits; - return Bits; - } else { - return static_cast<unsigned>(V); - } -} - -template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T> -inline bool ShiftRight(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) { - if (RHS >= V.bitWidth()) { - S.Stk.push<T>(T::from(0, V.bitWidth())); - } else { - S.Stk.push<T>(T::from(V >> RHS, V.bitWidth())); - } - return true; -} +template <PrimType NameL, PrimType NameR> +inline bool Shr(InterpState &S, CodePtr OpPC) { + using LT = typename PrimConv<NameL>::T; + using RT = typename PrimConv<NameR>::T; + const auto &RHS = S.Stk.pop<RT>(); + const auto &LHS = S.Stk.pop<LT>(); + const unsigned Bits = LHS.bitWidth(); -template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T> -inline bool ShiftLeft(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) { - if (V.isSigned() && !S.getLangOpts().CPlusPlus20) { - // C++11 [expr.shift]p2: A signed left shift must have a non-negative - // operand, and must not overflow the corresponding unsigned type. - // C++2a [expr.shift]p2: E1 << E2 is the unique value congruent to - // E1 x 2^E2 module 2^N. - if (V.isNegative()) { - const Expr *E = S.Current->getExpr(OpPC); - S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << V.toAPSInt(); - } else if (V.countLeadingZeros() < RHS) { - S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards); - } - } + if (!CheckShift<RT>(S, OpPC, RHS, Bits)) + return false; - if (V.bitWidth() == 1) { - S.Stk.push<T>(V); - } else if (RHS >= V.bitWidth()) { - S.Stk.push<T>(T::from(0, V.bitWidth())); - } else { - S.Stk.push<T>(T::from(V.toUnsigned() << RHS, V.bitWidth())); - } + unsigned URHS = static_cast<unsigned>(RHS); + S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) >> URHS, LHS.bitWidth())); return true; } -template <PrimType TL, PrimType TR> -inline bool Shr(InterpState &S, CodePtr OpPC) { - const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>(); - const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>(); +template <PrimType NameL, PrimType NameR> +inline bool Shl(InterpState &S, CodePtr OpPC) { + using LT = typename PrimConv<NameL>::T; + using RT = typename PrimConv<NameR>::T; + const auto &RHS = S.Stk.pop<RT>(); + const auto &LHS = S.Stk.pop<LT>(); const unsigned Bits = LHS.bitWidth(); - if (RHS.isSigned() && RHS.isNegative()) { - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); - return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS)); - } else { - return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS)); - } -} + if (!CheckShift<RT>(S, OpPC, RHS, Bits)) + return false; -template <PrimType TL, PrimType TR> -inline bool Shl(InterpState &S, CodePtr OpPC) { - const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>(); - const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>(); - const unsigned Bits = LHS.bitWidth(); + unsigned URHS = static_cast<unsigned>(RHS); + S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) << URHS, LHS.bitWidth())); - if (RHS.isSigned() && RHS.isNegative()) { - const SourceInfo &Loc = S.Current->getSource(OpPC); - S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); - return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS)); - } else { - return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS)); - } + return true; } //===----------------------------------------------------------------------===// @@ -950,26 +1253,56 @@ inline bool ExpandPtr(InterpState &S, CodePtr OpPC) { return true; } +inline bool Call(InterpState &S, CodePtr &PC, const Function *Func) { + auto NewFrame = std::make_unique<InterpFrame>(S, Func, PC); + Pointer ThisPtr; + if (Func->hasThisPointer()) { + ThisPtr = NewFrame->getThis(); + if (!CheckInvoke(S, PC, ThisPtr)) { + return false; + } + } + + if (!CheckCallable(S, PC, Func)) + return false; + + InterpFrame *FrameBefore = S.Current; + S.Current = NewFrame.get(); + + APValue CallResult; + // Note that we cannot assert(CallResult.hasValue()) here since + // Ret() above only sets the APValue if the curent frame doesn't + // have a caller set. + if (Interpret(S, CallResult)) { + NewFrame.release(); // Frame was delete'd already. + assert(S.Current == FrameBefore); + + // For constructors, check that all fields have been initialized. + if (Func->isConstructor() && !CheckCtorCall(S, PC, ThisPtr)) + return false; + + return true; + } + + // Interpreting the function failed somehow. Reset to + // previous state. + S.Current = FrameBefore; + return false; +} + //===----------------------------------------------------------------------===// // Read opcode arguments //===----------------------------------------------------------------------===// -template <typename T> -inline std::enable_if_t<!std::is_pointer<T>::value, T> ReadArg(InterpState &S, - CodePtr OpPC) { - return OpPC.read<T>(); -} - -template <typename T> -inline std::enable_if_t<std::is_pointer<T>::value, T> ReadArg(InterpState &S, - CodePtr OpPC) { - uint32_t ID = OpPC.read<uint32_t>(); - return reinterpret_cast<T>(S.P.getNativePointer(ID)); +template <typename T> inline T ReadArg(InterpState &S, CodePtr &OpPC) { + if constexpr (std::is_pointer<T>::value) { + uint32_t ID = OpPC.read<uint32_t>(); + return reinterpret_cast<T>(S.P.getNativePointer(ID)); + } else { + return OpPC.read<T>(); + } } -/// Interpreter entry point. -bool Interpret(InterpState &S, APValue &Result); - } // namespace interp } // namespace clang diff --git a/contrib/llvm-project/clang/lib/AST/Interp/InterpBlock.h b/contrib/llvm-project/clang/lib/AST/Interp/InterpBlock.h index 2d5386e60b8c..f790c50a9123 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/InterpBlock.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/InterpBlock.h @@ -31,11 +31,25 @@ enum PrimType : unsigned; /// A memory block, either on the stack or in the heap. /// -/// The storage described by the block immediately follows it in memory. -class Block { +/// The storage described by the block is immediately followed by +/// optional metadata, which is followed by the actual data. +/// +/// Block* rawData() data() +/// │ │ │ +/// │ │ │ +/// ▼ ▼ ▼ +/// ┌───────────────┬─────────────────────────┬─────────────────┐ +/// │ Block │ Metadata │ Data │ +/// │ sizeof(Block) │ Desc->getMetadataSize() │ Desc->getSize() │ +/// └───────────────┴─────────────────────────┴─────────────────┘ +/// +/// Desc->getAllocSize() describes the size after the Block, i.e. +/// the data size and the metadata size. +/// +class Block final { public: // Creates a new block. - Block(const llvm::Optional<unsigned> &DeclID, Descriptor *Desc, + Block(const std::optional<unsigned> &DeclID, Descriptor *Desc, bool IsStatic = false, bool IsExtern = false) : DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {} @@ -56,10 +70,27 @@ public: /// Returns the size of the block. InterpSize getSize() const { return Desc->getAllocSize(); } /// Returns the declaration ID. - llvm::Optional<unsigned> getDeclID() const { return DeclID; } + std::optional<unsigned> getDeclID() const { return DeclID; } /// Returns a pointer to the stored data. - char *data() { return reinterpret_cast<char *>(this + 1); } + /// You are allowed to read Desc->getSize() bytes from this address. + char *data() { + // rawData might contain metadata as well. + size_t DataOffset = Desc->getMetadataSize(); + return rawData() + DataOffset; + } + const char *data() const { + // rawData might contain metadata as well. + size_t DataOffset = Desc->getMetadataSize(); + return rawData() + DataOffset; + } + + /// Returns a pointer to the raw data, including metadata. + /// You are allowed to read Desc->getAllocSize() bytes from this address. + char *rawData() { return reinterpret_cast<char *>(this) + sizeof(Block); } + const char *rawData() const { + return reinterpret_cast<const char *>(this) + sizeof(Block); + } /// Returns a view over the data. template <typename T> @@ -67,12 +98,18 @@ public: /// Invokes the constructor. void invokeCtor() { - std::memset(data(), 0, getSize()); + std::memset(rawData(), 0, Desc->getAllocSize()); if (Desc->CtorFn) Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable, /*isActive=*/true, Desc); } + // Invokes the Destructor. + void invokeDtor() { + if (Desc->DtorFn) + Desc->DtorFn(this, data(), Desc); + } + protected: friend class Pointer; friend class DeadBlock; @@ -92,7 +129,7 @@ protected: /// Start of the chain of pointers. Pointer *Pointers = nullptr; /// Unique identifier of the declaration. - llvm::Optional<unsigned> DeclID; + std::optional<unsigned> DeclID; /// Flag indicating if the block has static storage duration. bool IsStatic = false; /// Flag indicating if the block is an extern. @@ -107,7 +144,7 @@ protected: /// /// Dead blocks are chained in a double-linked list to deallocate them /// whenever pointers become dead. -class DeadBlock { +class DeadBlock final { public: /// Copies the block. DeadBlock(DeadBlock *&Root, Block *Blk); diff --git a/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.cpp b/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.cpp index 9d01bf0333fe..40644c538c6a 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.cpp @@ -7,37 +7,68 @@ //===----------------------------------------------------------------------===// #include "InterpFrame.h" +#include "Boolean.h" #include "Function.h" -#include "Interp.h" #include "InterpStack.h" +#include "InterpState.h" +#include "Pointer.h" #include "PrimType.h" #include "Program.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" using namespace clang; using namespace clang::interp; -InterpFrame::InterpFrame(InterpState &S, Function *Func, InterpFrame *Caller, - CodePtr RetPC, Pointer &&This) - : Caller(Caller), S(S), Func(Func), This(std::move(This)), RetPC(RetPC), +InterpFrame::InterpFrame(InterpState &S, const Function *Func, + InterpFrame *Caller, CodePtr RetPC) + : Caller(Caller), S(S), Func(Func), RetPC(RetPC), ArgSize(Func ? Func->getArgSize() : 0), Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) { - if (Func) { - if (unsigned FrameSize = Func->getFrameSize()) { - Locals = std::make_unique<char[]>(FrameSize); - for (auto &Scope : Func->scopes()) { - for (auto &Local : Scope.locals()) { - Block *B = new (localBlock(Local.Offset)) Block(Local.Desc); - B->invokeCtor(); - } - } + if (!Func) + return; + + unsigned FrameSize = Func->getFrameSize(); + if (FrameSize == 0) + return; + + Locals = std::make_unique<char[]>(FrameSize); + for (auto &Scope : Func->scopes()) { + for (auto &Local : Scope.locals()) { + Block *B = new (localBlock(Local.Offset)) Block(Local.Desc); + B->invokeCtor(); + InlineDescriptor *ID = localInlineDesc(Local.Offset); + ID->Desc = Local.Desc; + ID->IsActive = true; + ID->Offset = sizeof(InlineDescriptor); + ID->IsBase = false; + ID->IsFieldMutable = false; + ID->IsConst = false; + ID->IsInitialized = false; } } } +InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC) + : InterpFrame(S, Func, S.Current, RetPC) { + // As per our calling convention, the this pointer is + // part of the ArgSize. + // If the function has RVO, the RVO pointer is first. + // If the fuction has a This pointer, that one is next. + // Then follow the actual arguments (but those are handled + // in getParamPointer()). + if (Func->hasRVO()) + RVOPtr = stackRef<Pointer>(0); + + if (Func->hasThisPointer()) { + if (Func->hasRVO()) + This = stackRef<Pointer>(sizeof(Pointer)); + else + This = stackRef<Pointer>(0); + } +} + InterpFrame::~InterpFrame() { - if (Func && Func->isConstructor() && This.isBaseClass()) - This.initialize(); for (auto &Param : Params) S.deallocate(reinterpret_cast<Block *>(Param.second.get())); } @@ -95,17 +126,17 @@ void print(llvm::raw_ostream &OS, const Pointer &P, ASTContext &Ctx, } printDesc(P.getDeclDesc()); - for (auto It = Levels.rbegin(); It != Levels.rend(); ++It) { - if (It->inArray()) { - OS << "[" << It->expand().getIndex() << "]"; + for (const auto &It : Levels) { + if (It.inArray()) { + OS << "[" << It.expand().getIndex() << "]"; continue; } - if (auto Index = It->getIndex()) { + if (auto Index = It.getIndex()) { OS << " + " << Index; continue; } OS << "."; - printDesc(It->getFieldDesc()); + printDesc(It.getFieldDesc()); } } @@ -117,16 +148,15 @@ void InterpFrame::describe(llvm::raw_ostream &OS) { OS << "->"; } OS << *F << "("; - unsigned Off = Func->hasRVO() ? primSize(PT_Ptr) : 0; + unsigned Off = 0; + + Off += Func->hasRVO() ? primSize(PT_Ptr) : 0; + Off += Func->hasThisPointer() ? primSize(PT_Ptr) : 0; + for (unsigned I = 0, N = F->getNumParams(); I < N; ++I) { QualType Ty = F->getParamDecl(I)->getType(); - PrimType PrimTy; - if (llvm::Optional<PrimType> T = S.Ctx.classify(Ty)) { - PrimTy = *T; - } else { - PrimTy = PT_Ptr; - } + PrimType PrimTy = S.Ctx.classify(Ty).value_or(PT_Ptr); TYPE_SWITCH(PrimTy, print(OS, stackRef<T>(Off), S.getCtx(), Ty)); Off += align(primSize(PrimTy)); @@ -152,10 +182,10 @@ const FunctionDecl *InterpFrame::getCallee() const { return Func->getDecl(); } -Pointer InterpFrame::getLocalPointer(unsigned Offset) { +Pointer InterpFrame::getLocalPointer(unsigned Offset) const { assert(Offset < Func->getFrameSize() && "Invalid local offset."); - return Pointer( - reinterpret_cast<Block *>(Locals.get() + Offset - sizeof(Block))); + return Pointer(reinterpret_cast<Block *>(localBlock(Offset)), + sizeof(InlineDescriptor)); } Pointer InterpFrame::getParamPointer(unsigned Off) { diff --git a/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.h b/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.h index 304e2ad66537..bfa02c90ebec 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.h @@ -14,7 +14,6 @@ #define LLVM_CLANG_AST_INTERP_INTERPFRAME_H #include "Frame.h" -#include "Pointer.h" #include "Program.h" #include "State.h" #include <cstdint> @@ -24,6 +23,7 @@ namespace clang { namespace interp { class Function; class InterpState; +class Pointer; /// Frame storing local variables. class InterpFrame final : public Frame { @@ -32,8 +32,14 @@ public: InterpFrame *Caller; /// Creates a new frame for a method call. - InterpFrame(InterpState &S, Function *Func, InterpFrame *Caller, - CodePtr RetPC, Pointer &&This); + InterpFrame(InterpState &S, const Function *Func, InterpFrame *Caller, + CodePtr RetPC); + + /// Creates a new frame with the values that make sense. + /// I.e., the caller is the current frame of S, + /// the This() pointer is the current Pointer on the top of S's stack, + /// and the RVO pointer is before that. + InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC); /// Destroys the frame, killing all live pointers to stack slots. ~InterpFrame(); @@ -57,26 +63,27 @@ public: const FunctionDecl *getCallee() const override; /// Returns the current function. - Function *getFunction() const { return Func; } + const Function *getFunction() const { return Func; } /// Returns the offset on the stack at which the frame starts. size_t getFrameOffset() const { return FrameOffset; } /// Returns the value of a local variable. - template <typename T> const T &getLocal(unsigned Offset) { + template <typename T> const T &getLocal(unsigned Offset) const { return localRef<T>(Offset); } /// Mutates a local variable. template <typename T> void setLocal(unsigned Offset, const T &Value) { localRef<T>(Offset) = Value; + localInlineDesc(Offset)->IsInitialized = true; } /// Returns a pointer to a local variables. - Pointer getLocalPointer(unsigned Offset); + Pointer getLocalPointer(unsigned Offset) const; /// Returns the value of an argument. - template <typename T> const T &getParam(unsigned Offset) { + template <typename T> const T &getParam(unsigned Offset) const { auto Pt = Params.find(Offset); if (Pt == Params.end()) { return stackRef<T>(Offset); @@ -96,6 +103,9 @@ public: /// Returns the 'this' pointer. const Pointer &getThis() const { return This; } + /// Returns the RVO pointer, if the Function has one. + const Pointer &getRVOPtr() const { return RVOPtr; } + /// Checks if the frame is a root frame - return should quit the interpreter. bool isRoot() const { return !Func; } @@ -112,27 +122,35 @@ public: private: /// Returns an original argument from the stack. - template <typename T> const T &stackRef(unsigned Offset) { + template <typename T> const T &stackRef(unsigned Offset) const { + assert(Args); return *reinterpret_cast<const T *>(Args - ArgSize + Offset); } /// Returns an offset to a local. - template <typename T> T &localRef(unsigned Offset) { - return *reinterpret_cast<T *>(Locals.get() + Offset); + template <typename T> T &localRef(unsigned Offset) const { + return getLocalPointer(Offset).deref<T>(); } /// Returns a pointer to a local's block. - void *localBlock(unsigned Offset) { + void *localBlock(unsigned Offset) const { return Locals.get() + Offset - sizeof(Block); } + // Returns the inline descriptor of the local. + InlineDescriptor *localInlineDesc(unsigned Offset) const { + return reinterpret_cast<InlineDescriptor *>(Locals.get() + Offset); + } + private: /// Reference to the interpreter state. InterpState &S; /// Reference to the function being executed. - Function *Func; + const Function *Func; /// Current object pointer for methods. Pointer This; + /// Pointer the non-primitive return value gets constructed in. + Pointer RVOPtr; /// Return address. CodePtr RetPC; /// The size of all the arguments. diff --git a/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.cpp b/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.cpp index 5c803f3d9424..7fe678e62192 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.cpp @@ -46,7 +46,7 @@ void *InterpStack::grow(size_t Size) { return Object; } -void *InterpStack::peek(size_t Size) { +void *InterpStack::peek(size_t Size) const { assert(Chunk && "Stack is empty!"); StackChunk *Ptr = Chunk; diff --git a/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.h b/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.h index b02d3c6a34b0..3adaad96515e 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.h @@ -13,7 +13,9 @@ #ifndef LLVM_CLANG_AST_INTERP_INTERPSTACK_H #define LLVM_CLANG_AST_INTERP_INTERPSTACK_H +#include "PrimType.h" #include <memory> +#include <vector> namespace clang { namespace interp { @@ -29,10 +31,18 @@ public: /// Constructs a value in place on the top of the stack. template <typename T, typename... Tys> void push(Tys &&... Args) { new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...); +#ifndef NDEBUG + ItemTypes.push_back(toPrimType<T>()); +#endif } /// Returns the value from the top of the stack and removes it. template <typename T> T pop() { +#ifndef NDEBUG + assert(!ItemTypes.empty()); + assert(ItemTypes.back() == toPrimType<T>()); + ItemTypes.pop_back(); +#endif auto *Ptr = &peek<T>(); auto Value = std::move(*Ptr); Ptr->~T(); @@ -42,18 +52,22 @@ public: /// Discards the top value from the stack. template <typename T> void discard() { +#ifndef NDEBUG + assert(ItemTypes.back() == toPrimType<T>()); + ItemTypes.pop_back(); +#endif auto *Ptr = &peek<T>(); Ptr->~T(); shrink(aligned_size<T>()); } /// Returns a reference to the value on the top of the stack. - template <typename T> T &peek() { + template <typename T> T &peek() const { return *reinterpret_cast<T *>(peek(aligned_size<T>())); } /// Returns a pointer to the top object. - void *top() { return Chunk ? peek(0) : nullptr; } + void *top() const { return Chunk ? peek(0) : nullptr; } /// Returns the size of the stack in bytes. size_t size() const { return StackSize; } @@ -61,6 +75,9 @@ public: /// Clears the stack without calling any destructors. void clear(); + // Returns whether the stack is empty. + bool empty() const { return StackSize == 0; } + private: /// All stack slots are aligned to the native pointer alignment for storage. /// The size of an object is rounded up to a pointer alignment multiple. @@ -72,7 +89,7 @@ private: /// Grows the stack to accommodate a value and returns a pointer to it. void *grow(size_t Size); /// Returns a pointer from the top of the stack. - void *peek(size_t Size); + void *peek(size_t Size) const; /// Shrinks the stack. void shrink(size_t Size); @@ -94,10 +111,13 @@ private: : Next(nullptr), Prev(Prev), End(reinterpret_cast<char *>(this + 1)) {} /// Returns the size of the chunk, minus the header. - size_t size() { return End - start(); } + size_t size() const { return End - start(); } /// Returns a pointer to the start of the data region. char *start() { return reinterpret_cast<char *>(this + 1); } + const char *start() const { + return reinterpret_cast<const char *>(this + 1); + } }; static_assert(sizeof(StackChunk) < ChunkSize, "Invalid chunk size"); @@ -105,6 +125,45 @@ private: StackChunk *Chunk = nullptr; /// Total size of the stack. size_t StackSize = 0; + +#ifndef NDEBUG + /// vector recording the type of data we pushed into the stack. + std::vector<PrimType> ItemTypes; + + template <typename T> static constexpr PrimType toPrimType() { + if constexpr (std::is_same_v<T, Pointer>) + return PT_Ptr; + else if constexpr (std::is_same_v<T, bool> || + std::is_same_v<T, Boolean>) + return PT_Bool; + else if constexpr (std::is_same_v<T, int8_t> || + std::is_same_v<T, Integral<8, true>>) + return PT_Sint8; + else if constexpr (std::is_same_v<T, uint8_t> || + std::is_same_v<T, Integral<8, false>>) + return PT_Uint8; + else if constexpr (std::is_same_v<T, int16_t> || + std::is_same_v<T, Integral<16, true>>) + return PT_Sint16; + else if constexpr (std::is_same_v<T, uint16_t> || + std::is_same_v<T, Integral<16, false>>) + return PT_Uint16; + else if constexpr (std::is_same_v<T, int32_t> || + std::is_same_v<T, Integral<32, true>>) + return PT_Sint32; + else if constexpr (std::is_same_v<T, uint32_t> || + std::is_same_v<T, Integral<32, false>>) + return PT_Uint32; + else if constexpr (std::is_same_v<T, int64_t> || + std::is_same_v<T, Integral<64, true>>) + return PT_Sint64; + else if constexpr (std::is_same_v<T, uint64_t> || + std::is_same_v<T, Integral<64, false>>) + return PT_Uint64; + + llvm_unreachable("unknown type push()'ed into InterpStack"); + } +#endif }; } // namespace interp diff --git a/contrib/llvm-project/clang/lib/AST/Interp/InterpState.h b/contrib/llvm-project/clang/lib/AST/Interp/InterpState.h index 57e36c4c63ea..033080637385 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/InterpState.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/InterpState.h @@ -65,6 +65,7 @@ public: bool noteUndefinedBehavior() override { return Parent.noteUndefinedBehavior(); } + bool inConstantContext() const { return Parent.InConstantContext; } bool hasActiveDiagnostic() override { return Parent.hasActiveDiagnostic(); } void setActiveDiagnostic(bool Flag) override { Parent.setActiveDiagnostic(Flag); @@ -81,7 +82,7 @@ public: void deallocate(Block *B); /// Delegates source mapping to the mapper. - SourceInfo getSource(Function *F, CodePtr PC) const override { + SourceInfo getSource(const Function *F, CodePtr PC) const override { return M ? M->getSource(F, PC) : F->getSource(PC); } diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Opcodes.td b/contrib/llvm-project/clang/lib/AST/Interp/Opcodes.td index 638d5b3d2357..058475b2d399 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Opcodes.td +++ b/contrib/llvm-project/clang/lib/AST/Interp/Opcodes.td @@ -42,18 +42,8 @@ def ArgSint64 : ArgType { let Name = "int64_t"; } def ArgUint64 : ArgType { let Name = "uint64_t"; } def ArgBool : ArgType { let Name = "bool"; } -def ArgFunction : ArgType { let Name = "Function *"; } -def ArgRecord : ArgType { let Name = "Record *"; } - -def ArgSema : ArgType { let Name = "const fltSemantics *"; } - -def ArgExpr : ArgType { let Name = "const Expr *"; } -def ArgFloatingLiteral : ArgType { let Name = "const FloatingLiteral *"; } -def ArgCXXMethodDecl : ArgType { let Name = "const CXXMethodDecl *"; } -def ArgFunctionDecl : ArgType { let Name = "const FunctionDecl *"; } +def ArgFunction : ArgType { let Name = "const Function *"; } def ArgRecordDecl : ArgType { let Name = "const RecordDecl *"; } -def ArgCXXRecordDecl : ArgType { let Name = "const CXXRecordDecl *"; } -def ArgValueDecl : ArgType { let Name = "const ValueDecl *"; } def ArgRecordField : ArgType { let Name = "const Record::Field *"; } //===----------------------------------------------------------------------===// @@ -64,15 +54,28 @@ class TypeClass { list<Type> Types; } -def AluTypeClass : TypeClass { +def NumberTypeClass : TypeClass { + let Types = [Sint8, Uint8, Sint16, Uint16, Sint32, + Uint32, Sint64, Uint64]; +} + +def IntegerTypeClass : TypeClass { let Types = [Sint8, Uint8, Sint16, Uint16, Sint32, - Uint32, Sint64, Uint64, Bool]; + Uint32, Sint64, Uint64]; +} + +def AluTypeClass : TypeClass { + let Types = !listconcat(NumberTypeClass.Types, [Bool]); } def PtrTypeClass : TypeClass { let Types = [Ptr]; } +def BoolTypeClass : TypeClass { + let Types = [Bool]; +} + def AllTypeClass : TypeClass { let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types); } @@ -105,6 +108,11 @@ class AluOpcode : Opcode { let HasGroup = 1; } +class IntegerOpcode : Opcode { + let Types = [IntegerTypeClass]; + let HasGroup = 1; +} + //===----------------------------------------------------------------------===// // Jump opcodes //===----------------------------------------------------------------------===// @@ -149,6 +157,13 @@ def RetValue : Opcode { // [] -> EXIT def NoRet : Opcode {} + +def Call : Opcode { + let Args = [ArgFunction]; + let Types = []; + let ChangesPC = 1; +} + //===----------------------------------------------------------------------===// // Frame management //===----------------------------------------------------------------------===// @@ -183,6 +198,7 @@ def ConstBool : ConstOpcode<Bool, ArgBool>; // [] -> [Integer] def Zero : Opcode { let Types = [AluTypeClass]; + let HasGroup = 1; } // [] -> [Pointer] @@ -253,6 +269,9 @@ def GetPtrThisVirtBase : Opcode { // [] -> [Pointer] def This : Opcode; +// [] -> [Pointer] +def RVOPtr : Opcode; + // [Pointer] -> [Pointer] def NarrowPtr : Opcode; // [Pointer] -> [Pointer] @@ -374,6 +393,12 @@ def AddOffset : AluOpcode; // [Pointer, Integral] -> [Pointer] def SubOffset : AluOpcode; +// Pointer, Pointer] - [Integral] +def SubPtr : Opcode { + let Types = [IntegerTypeClass]; + let HasGroup = 1; +} + //===----------------------------------------------------------------------===// // Binary operators. //===----------------------------------------------------------------------===// @@ -382,6 +407,73 @@ def SubOffset : AluOpcode; def Sub : AluOpcode; def Add : AluOpcode; def Mul : AluOpcode; +def Rem : Opcode { + let Types = [NumberTypeClass]; + let HasGroup = 1; +} + +def Shl : Opcode { + let Types = [IntegerTypeClass, IntegerTypeClass]; + let HasGroup = 1; +} + +def Shr : Opcode { + let Types = [IntegerTypeClass, IntegerTypeClass]; + let HasGroup = 1; +} + +def BitAnd : IntegerOpcode; +def BitOr : IntegerOpcode; +def Div : Opcode { + let Types = [NumberTypeClass]; + let HasGroup = 1; +} +def BitXor : IntegerOpcode; + +//===----------------------------------------------------------------------===// +// Unary operators. +//===----------------------------------------------------------------------===// + +// [Real] -> [Real] +def Inv: Opcode { + let Types = [BoolTypeClass]; + let HasGroup = 1; +} + +def Inc: IntegerOpcode; +def IncPop : IntegerOpcode; +def Dec: IntegerOpcode; +def DecPop: IntegerOpcode; + +// [Real] -> [Real] +def Neg: Opcode { + let Types = [AluTypeClass]; + let HasGroup = 1; +} + +// [Real] -> [Real] +def Comp: Opcode { + let Types = [NumberTypeClass]; + let HasGroup = 1; +} + +//===----------------------------------------------------------------------===// +// Cast. +//===----------------------------------------------------------------------===// +// TODO: Expand this to handle casts between more types. + +def FromCastTypeClass : TypeClass { + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool]; +} + +def ToCastTypeClass : TypeClass { + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool]; +} + +def Cast: Opcode { + let Types = [FromCastTypeClass, ToCastTypeClass]; + let HasGroup = 1; +} //===----------------------------------------------------------------------===// // Comparison opcodes. diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp index ef2638e2a36b..fd8c98fae039 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp @@ -16,6 +16,9 @@ using namespace clang::interp; Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {} +Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset) + : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {} + Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {} Pointer::Pointer(Pointer &&P) @@ -106,7 +109,7 @@ APValue Pointer::toAPValue() const { // Build the path into the object. Pointer Ptr = *this; - while (Ptr.isField()) { + while (Ptr.isField() || Ptr.isArrayElement()) { if (Ptr.isArrayElement()) { Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex())); Ptr = Ptr.getArray(); @@ -129,14 +132,21 @@ APValue Pointer::toAPValue() const { } } + // We assemble the LValuePath starting from the innermost pointer to the + // outermost one. SO in a.b.c, the first element in Path will refer to + // the field 'c', while later code expects it to refer to 'a'. + // Just invert the order of the elements. + std::reverse(Path.begin(), Path.end()); + return APValue(Base, Offset, Path, IsOnePastEnd, IsNullPtr); } bool Pointer::isInitialized() const { assert(Pointee && "Cannot check if null pointer was initialized"); Descriptor *Desc = getFieldDesc(); + assert(Desc); if (Desc->isPrimitiveArray()) { - if (Pointee->IsStatic) + if (isStatic() && Base == 0) return true; // Primitive array field are stored in a bitset. InitMap *Map = getInitMap(); @@ -154,8 +164,14 @@ bool Pointer::isInitialized() const { void Pointer::initialize() const { assert(Pointee && "Cannot initialize null pointer"); Descriptor *Desc = getFieldDesc(); - if (Desc->isPrimitiveArray()) { - if (!Pointee->IsStatic) { + + assert(Desc); + if (Desc->isArray()) { + if (Desc->isPrimitiveArray()) { + // Primitive global arrays don't have an initmap. + if (isStatic() && Base == 0) + return; + // Primitive array initializer. InitMap *&Map = getInitMap(); if (Map == (InitMap *)-1) @@ -189,5 +205,5 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) { } bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) { - return A.Base == B.Base && A.getFieldDesc()->IsArray; + return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray; } diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Pointer.h b/contrib/llvm-project/clang/lib/AST/Interp/Pointer.h index 587531aec82a..1462d01c2412 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Pointer.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/Pointer.h @@ -33,14 +33,40 @@ enum PrimType : unsigned; /// /// This object can be allocated into interpreter stack frames. If pointing to /// a live block, it is a link in the chain of pointers pointing to the block. +/// +/// In the simplest form, a Pointer has a Block* (the pointee) and both Base +/// and Offset are 0, which means it will point to raw data. +/// +/// The Base field is used to access metadata about the data. For primitive +/// arrays, the Base is followed by an InitMap. In a variety of cases, the +/// Base is preceded by an InlineDescriptor, which is used to track the +/// initialization state, among other things. +/// +/// The Offset field is used to access the actual data. In other words, the +/// data the pointer decribes can be found at +/// Pointee->rawData() + Pointer.Offset. +/// +/// +/// Pointee Offset +/// │ │ +/// │ │ +/// ▼ ▼ +/// ┌───────┬────────────┬─────────┬────────────────────────────┐ +/// │ Block │ InlineDesc │ InitMap │ Actual Data │ +/// └───────┴────────────┴─────────┴────────────────────────────┘ +/// ▲ +/// │ +/// │ +/// Base class Pointer { private: - static constexpr unsigned PastEndMark = (unsigned)-1; - static constexpr unsigned RootPtrMark = (unsigned)-1; + static constexpr unsigned PastEndMark = ~0u; + static constexpr unsigned RootPtrMark = ~0u; public: Pointer() {} Pointer(Block *B); + Pointer(Block *B, unsigned BaseAndOffset); Pointer(const Pointer &P); Pointer(Pointer &&P); ~Pointer(); @@ -216,6 +242,8 @@ public: /// Returns the record descriptor of a class. Record *getRecord() const { return getFieldDesc()->ElemRecord; } + // Returns the element record type, if this is a non-primive array. + Record *getElemRecord() const { return getFieldDesc()->ElemDesc->ElemRecord; } /// Returns the field information. const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); } @@ -232,7 +260,9 @@ public: bool isStaticTemporary() const { return isStatic() && isTemporary(); } /// Checks if the field is mutable. - bool isMutable() const { return Base != 0 && getInlineDesc()->IsMutable; } + bool isMutable() const { + return Base != 0 && getInlineDesc()->IsFieldMutable; + } /// Checks if an object was initialized. bool isInitialized() const; /// Checks if the object is active. @@ -246,7 +276,7 @@ public: } /// Returns the declaration ID. - llvm::Optional<unsigned> getDeclID() const { return Pointee->getDeclID(); } + std::optional<unsigned> getDeclID() const { return Pointee->getDeclID(); } /// Returns the byte offset from the start. unsigned getByteOffset() const { @@ -276,12 +306,12 @@ public: /// Dereferences the pointer, if it's live. template <typename T> T &deref() const { assert(isLive() && "Invalid pointer"); - return *reinterpret_cast<T *>(Pointee->data() + Offset); + return *reinterpret_cast<T *>(Pointee->rawData() + Offset); } /// Dereferences a primitive element. template <typename T> T &elem(unsigned I) const { - return reinterpret_cast<T *>(Pointee->data())[I]; + return reinterpret_cast<T *>(Pointee->rawData())[I]; } /// Initializes a field. @@ -298,7 +328,7 @@ public: /// Prints the pointer. void print(llvm::raw_ostream &OS) const { - OS << "{" << Base << ", " << Offset << ", "; + OS << Pointee << " {" << Base << ", " << Offset << ", "; if (Pointee) OS << Pointee->getSize(); else @@ -318,12 +348,13 @@ private: /// Returns a descriptor at a given offset. InlineDescriptor *getDescriptor(unsigned Offset) const { assert(Offset != 0 && "Not a nested pointer"); - return reinterpret_cast<InlineDescriptor *>(Pointee->data() + Offset) - 1; + return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) - + 1; } /// Returns a reference to the pointer which stores the initialization map. InitMap *&getInitMap() const { - return *reinterpret_cast<InitMap **>(Pointee->data() + Base); + return *reinterpret_cast<InitMap **>(Pointee->rawData() + Base); } /// The block the pointer is pointing to. diff --git a/contrib/llvm-project/clang/lib/AST/Interp/PrimType.cpp b/contrib/llvm-project/clang/lib/AST/Interp/PrimType.cpp index 082bfaf3c207..eda90e1c36c2 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/PrimType.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/PrimType.cpp @@ -1,4 +1,4 @@ -//===--- Type.cpp - Types for the constexpr VM ------------------*- C++ -*-===// +//===--- PrimType.cpp - Types for the constexpr VM --------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "PrimType.h" +#include "Boolean.h" +#include "Pointer.h" using namespace clang; using namespace clang::interp; diff --git a/contrib/llvm-project/clang/lib/AST/Interp/PrimType.h b/contrib/llvm-project/clang/lib/AST/Interp/PrimType.h index de4bf9bf802e..c8f2a600fb3c 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/PrimType.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/PrimType.h @@ -13,16 +13,17 @@ #ifndef LLVM_CLANG_AST_INTERP_TYPE_H #define LLVM_CLANG_AST_INTERP_TYPE_H +#include "Integral.h" #include <climits> #include <cstddef> #include <cstdint> -#include "Boolean.h" -#include "Integral.h" -#include "Pointer.h" namespace clang { namespace interp { +class Pointer; +class Boolean; + /// Enumeration of the primitive types of the VM. enum PrimType : unsigned { PT_Sint8, @@ -58,6 +59,13 @@ constexpr size_t align(size_t Size) { return ((Size + alignof(void *) - 1) / alignof(void *)) * alignof(void *); } +constexpr bool aligned(uintptr_t Value) { return Value == align(Value); } +static_assert(aligned(sizeof(void *))); + +static inline bool aligned(const void *P) { + return aligned(reinterpret_cast<uintptr_t>(P)); +} + inline bool isPrimitiveIntegral(PrimType Type) { switch (Type) { case PT_Bool: diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Program.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Program.cpp index e310c9678140..5305ddd8de18 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Program.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/Program.cpp @@ -53,10 +53,11 @@ unsigned Program::createGlobalString(const StringLiteral *S) { } // Create a descriptor for the string. - Descriptor *Desc = allocateDescriptor(S, CharType, S->getLength() + 1, - /*isConst=*/true, - /*isTemporary=*/false, - /*isMutable=*/false); + Descriptor *Desc = + allocateDescriptor(S, CharType, std::nullopt, S->getLength() + 1, + /*isConst=*/true, + /*isTemporary=*/false, + /*isMutable=*/false); // Allocate storage for the string. // The byte length does not include the null terminator. @@ -64,6 +65,7 @@ unsigned Program::createGlobalString(const StringLiteral *S) { unsigned Sz = Desc->getAllocSize(); auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true, /*isExtern=*/false); + G->block()->invokeCtor(); Globals.push_back(G); // Construct the string in storage. @@ -99,13 +101,13 @@ Pointer Program::getPtrGlobal(unsigned Idx) { return Pointer(Globals[Idx]->block()); } -llvm::Optional<unsigned> Program::getGlobal(const ValueDecl *VD) { +std::optional<unsigned> Program::getGlobal(const ValueDecl *VD) { auto It = GlobalIndices.find(VD); if (It != GlobalIndices.end()) return It->second; // Find any previous declarations which were already evaluated. - llvm::Optional<unsigned> Index; + std::optional<unsigned> Index; for (const Decl *P = VD; P; P = P->getPreviousDecl()) { auto It = GlobalIndices.find(P); if (It != GlobalIndices.end()) { @@ -123,18 +125,19 @@ llvm::Optional<unsigned> Program::getGlobal(const ValueDecl *VD) { return Index; } -llvm::Optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD) { +std::optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD, + const Expr *Init) { if (auto Idx = getGlobal(VD)) return Idx; - if (auto Idx = createGlobal(VD)) { + if (auto Idx = createGlobal(VD, Init)) { GlobalIndices[VD] = *Idx; return Idx; } return {}; } -llvm::Optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) { +std::optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) { auto &ASTCtx = Ctx.getASTContext(); // Create a pointer to an incomplete array of the specified elements. @@ -153,7 +156,9 @@ llvm::Optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) { return {}; } -llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) { +std::optional<unsigned> Program::createGlobal(const ValueDecl *VD, + const Expr *Init) { + assert(!getGlobal(VD)); bool IsStatic, IsExtern; if (auto *Var = dyn_cast<VarDecl>(VD)) { IsStatic = !Var->hasLocalStorage(); @@ -162,7 +167,7 @@ llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) { IsStatic = false; IsExtern = true; } - if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern)) { + if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern, Init)) { for (const Decl *P = VD; P; P = P->getPreviousDecl()) GlobalIndices[P] = *Idx; return *Idx; @@ -170,20 +175,22 @@ llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) { return {}; } -llvm::Optional<unsigned> Program::createGlobal(const Expr *E) { +std::optional<unsigned> Program::createGlobal(const Expr *E) { return createGlobal(E, E->getType(), /*isStatic=*/true, /*isExtern=*/false); } -llvm::Optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, - bool IsStatic, bool IsExtern) { +std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, + bool IsStatic, bool IsExtern, + const Expr *Init) { // Create a descriptor for the global. Descriptor *Desc; const bool IsConst = Ty.isConstQualified(); const bool IsTemporary = D.dyn_cast<const Expr *>(); if (auto T = Ctx.classify(Ty)) { - Desc = createDescriptor(D, *T, IsConst, IsTemporary); + Desc = createDescriptor(D, *T, std::nullopt, IsConst, IsTemporary); } else { - Desc = createDescriptor(D, Ty.getTypePtr(), IsConst, IsTemporary); + Desc = createDescriptor(D, Ty.getTypePtr(), std::nullopt, IsConst, + IsTemporary); } if (!Desc) return {}; @@ -201,24 +208,12 @@ llvm::Optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty, } Function *Program::getFunction(const FunctionDecl *F) { - F = F->getDefinition(); + F = F->getCanonicalDecl(); + assert(F); auto It = Funcs.find(F); return It == Funcs.end() ? nullptr : It->second.get(); } -llvm::Expected<Function *> Program::getOrCreateFunction(const FunctionDecl *F) { - if (Function *Func = getFunction(F)) { - return Func; - } - - // Try to compile the function if it wasn't compiled yet. - if (const FunctionDecl *FD = F->getDefinition()) - return ByteCodeStmtGen<ByteCodeEmitter>(Ctx, *this).compileFunc(FD); - - // A relocation which traps if not resolved. - return nullptr; -} - Record *Program::getOrCreateRecord(const RecordDecl *RD) { // Use the actual definition as a key. RD = RD->getDefinition(); @@ -231,8 +226,13 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { return It->second; } + // We insert nullptr now and replace that later, so recursive calls + // to this function with the same RecordDecl don't run into + // infinite recursion. + Records.insert({RD, nullptr}); + // Number of bytes required by fields and base classes. - unsigned Size = 0; + unsigned BaseSize = 0; // Number of bytes required by virtual base. unsigned VirtSize = 0; @@ -240,7 +240,7 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { auto GetBaseDesc = [this](const RecordDecl *BD, Record *BR) -> Descriptor * { if (!BR) return nullptr; - return allocateDescriptor(BD, BR, /*isConst=*/false, + return allocateDescriptor(BD, BR, std::nullopt, /*isConst=*/false, /*isTemporary=*/false, /*isMutable=*/false); }; @@ -256,9 +256,9 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { const RecordDecl *BD = Spec.getType()->castAs<RecordType>()->getDecl(); Record *BR = getOrCreateRecord(BD); if (Descriptor *Desc = GetBaseDesc(BD, BR)) { - Size += align(sizeof(InlineDescriptor)); - Bases.push_back({BD, Size, Desc, BR}); - Size += align(BR->getSize()); + BaseSize += align(sizeof(InlineDescriptor)); + Bases.push_back({BD, BaseSize, Desc, BR}); + BaseSize += align(BR->getSize()); continue; } return nullptr; @@ -282,39 +282,41 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) { Record::FieldList Fields; for (const FieldDecl *FD : RD->fields()) { // Reserve space for the field's descriptor and the offset. - Size += align(sizeof(InlineDescriptor)); + BaseSize += align(sizeof(InlineDescriptor)); // Classify the field and add its metadata. QualType FT = FD->getType(); const bool IsConst = FT.isConstQualified(); const bool IsMutable = FD->isMutable(); Descriptor *Desc; - if (llvm::Optional<PrimType> T = Ctx.classify(FT)) { - Desc = createDescriptor(FD, *T, IsConst, /*isTemporary=*/false, - IsMutable); + if (std::optional<PrimType> T = Ctx.classify(FT)) { + Desc = createDescriptor(FD, *T, std::nullopt, IsConst, + /*isTemporary=*/false, IsMutable); } else { - Desc = createDescriptor(FD, FT.getTypePtr(), IsConst, + Desc = createDescriptor(FD, FT.getTypePtr(), std::nullopt, IsConst, /*isTemporary=*/false, IsMutable); } if (!Desc) return nullptr; - Fields.push_back({FD, Size, Desc}); - Size += align(Desc->getAllocSize()); + Fields.push_back({FD, BaseSize, Desc}); + BaseSize += align(Desc->getAllocSize()); } Record *R = new (Allocator) Record(RD, std::move(Bases), std::move(Fields), - std::move(VirtBases), VirtSize, Size); - Records.insert({RD, R}); + std::move(VirtBases), VirtSize, BaseSize); + Records[RD] = R; return R; } Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, + Descriptor::MetadataSize MDSize, bool IsConst, bool IsTemporary, - bool IsMutable) { + bool IsMutable, const Expr *Init) { // Classes and structures. if (auto *RT = Ty->getAs<RecordType>()) { if (auto *Record = getOrCreateRecord(RT->getDecl())) - return allocateDescriptor(D, Record, IsConst, IsTemporary, IsMutable); + return allocateDescriptor(D, Record, MDSize, IsConst, IsTemporary, + IsMutable); } // Arrays. @@ -323,38 +325,39 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, // Array of well-known bounds. if (auto CAT = dyn_cast<ConstantArrayType>(ArrayType)) { size_t NumElems = CAT->getSize().getZExtValue(); - if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) { + if (std::optional<PrimType> T = Ctx.classify(ElemTy)) { // Arrays of primitives. unsigned ElemSize = primSize(*T); if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) { return {}; } - return allocateDescriptor(D, *T, NumElems, IsConst, IsTemporary, + return allocateDescriptor(D, *T, MDSize, NumElems, IsConst, IsTemporary, IsMutable); } else { // Arrays of composites. In this case, the array is a list of pointers, // followed by the actual elements. - Descriptor *Desc = - createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); - if (!Desc) + Descriptor *ElemDesc = createDescriptor( + D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary); + if (!ElemDesc) return nullptr; - InterpSize ElemSize = Desc->getAllocSize() + sizeof(InlineDescriptor); + InterpSize ElemSize = + ElemDesc->getAllocSize() + sizeof(InlineDescriptor); if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) return {}; - return allocateDescriptor(D, Desc, NumElems, IsConst, IsTemporary, - IsMutable); + return allocateDescriptor(D, ElemDesc, MDSize, NumElems, IsConst, + IsTemporary, IsMutable); } } // Array of unknown bounds - cannot be accessed and pointer arithmetic // is forbidden on pointers to such objects. if (isa<IncompleteArrayType>(ArrayType)) { - if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) { + if (std::optional<PrimType> T = Ctx.classify(ElemTy)) { return allocateDescriptor(D, *T, IsTemporary, Descriptor::UnknownSize{}); } else { - Descriptor *Desc = - createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary); + Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(), MDSize, + IsConst, IsTemporary); if (!Desc) return nullptr; return allocateDescriptor(D, Desc, IsTemporary, @@ -366,13 +369,15 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty, // Atomic types. if (auto *AT = Ty->getAs<AtomicType>()) { const Type *InnerTy = AT->getValueType().getTypePtr(); - return createDescriptor(D, InnerTy, IsConst, IsTemporary, IsMutable); + return createDescriptor(D, InnerTy, MDSize, IsConst, IsTemporary, + IsMutable); } // Complex types - represented as arrays of elements. if (auto *CT = Ty->getAs<ComplexType>()) { PrimType ElemTy = *Ctx.classify(CT->getElementType()); - return allocateDescriptor(D, ElemTy, 2, IsConst, IsTemporary, IsMutable); + return allocateDescriptor(D, ElemTy, MDSize, 2, IsConst, IsTemporary, + IsMutable); } return nullptr; diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Program.h b/contrib/llvm-project/clang/lib/AST/Interp/Program.h index ca985af8ad30..5a80dd1ed748 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Program.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/Program.h @@ -37,10 +37,26 @@ class Context; class Record; /// The program contains and links the bytecode for all functions. -class Program { +class Program final { public: Program(Context &Ctx) : Ctx(Ctx) {} + ~Program() { + // Manually destroy all the blocks. They are almost all harmless, + // but primitive arrays might have an InitMap* heap allocated and + // that needs to be freed. + for (Global *G : Globals) + G->block()->invokeDtor(); + + // Records might actually allocate memory themselves, but they + // are allocated using a BumpPtrAllocator. Call their desctructors + // here manually so they are properly freeing their resources. + for (auto RecordPair : Records) { + if (Record *R = RecordPair.second) + R->~Record(); + } + } + /// Marshals a native pointer to an ID for embedding in bytecode. unsigned getOrCreateNativePointer(const void *Ptr); @@ -60,23 +76,25 @@ public: } /// Finds a global's index. - llvm::Optional<unsigned> getGlobal(const ValueDecl *VD); + std::optional<unsigned> getGlobal(const ValueDecl *VD); /// Returns or creates a global an creates an index to it. - llvm::Optional<unsigned> getOrCreateGlobal(const ValueDecl *VD); + std::optional<unsigned> getOrCreateGlobal(const ValueDecl *VD, + const Expr *Init = nullptr); /// Returns or creates a dummy value for parameters. - llvm::Optional<unsigned> getOrCreateDummy(const ParmVarDecl *PD); + std::optional<unsigned> getOrCreateDummy(const ParmVarDecl *PD); /// Creates a global and returns its index. - llvm::Optional<unsigned> createGlobal(const ValueDecl *VD); + std::optional<unsigned> createGlobal(const ValueDecl *VD, const Expr *E); /// Creates a global from a lifetime-extended temporary. - llvm::Optional<unsigned> createGlobal(const Expr *E); + std::optional<unsigned> createGlobal(const Expr *E); /// Creates a new function from a code range. template <typename... Ts> Function *createFunction(const FunctionDecl *Def, Ts &&... Args) { + Def = Def->getCanonicalDecl(); auto *Func = new Function(*this, Def, std::forward<Ts>(Args)...); Funcs.insert({Def, std::unique_ptr<Function>(Func)}); return Func; @@ -92,26 +110,23 @@ public: /// Returns a function. Function *getFunction(const FunctionDecl *F); - /// Returns a pointer to a function if it exists and can be compiled. - /// If a function couldn't be compiled, an error is returned. - /// If a function was not yet defined, a null pointer is returned. - llvm::Expected<Function *> getOrCreateFunction(const FunctionDecl *F); - /// Returns a record or creates one if it does not exist. Record *getOrCreateRecord(const RecordDecl *RD); /// Creates a descriptor for a primitive type. Descriptor *createDescriptor(const DeclTy &D, PrimType Type, - bool IsConst = false, - bool IsTemporary = false, + Descriptor::MetadataSize MDSize = std::nullopt, + bool IsConst = false, bool IsTemporary = false, bool IsMutable = false) { - return allocateDescriptor(D, Type, IsConst, IsTemporary, IsMutable); + return allocateDescriptor(D, Type, MDSize, IsConst, IsTemporary, IsMutable); } /// Creates a descriptor for a composite type. Descriptor *createDescriptor(const DeclTy &D, const Type *Ty, + Descriptor::MetadataSize MDSize = std::nullopt, bool IsConst = false, bool IsTemporary = false, - bool IsMutable = false); + bool IsMutable = false, + const Expr *Init = nullptr); /// Context to manage declaration lifetimes. class DeclScope { @@ -124,17 +139,18 @@ public: }; /// Returns the current declaration ID. - llvm::Optional<unsigned> getCurrentDecl() const { + std::optional<unsigned> getCurrentDecl() const { if (CurrentDeclaration == NoDeclaration) - return llvm::Optional<unsigned>{}; + return std::optional<unsigned>{}; return LastDeclaration; } private: friend class DeclScope; - llvm::Optional<unsigned> createGlobal(const DeclTy &D, QualType Ty, - bool IsStatic, bool IsExtern); + std::optional<unsigned> createGlobal(const DeclTy &D, QualType Ty, + bool IsStatic, bool IsExtern, + const Expr *Init = nullptr); /// Reference to the VM context. Context &Ctx; diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Record.h b/contrib/llvm-project/clang/lib/AST/Interp/Record.h index 9cdee9003752..1742cb1cc4ee 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Record.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/Record.h @@ -13,14 +13,16 @@ #ifndef LLVM_CLANG_AST_INTERP_RECORD_H #define LLVM_CLANG_AST_INTERP_RECORD_H -#include "Pointer.h" +#include "Descriptor.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" namespace clang { namespace interp { class Program; /// Structure/Class descriptor. -class Record { +class Record final { public: /// Describes a record field. struct Field { @@ -47,6 +49,8 @@ public: public: /// Returns the underlying declaration. const RecordDecl *getDecl() const { return Decl; } + /// Returns the name of the underlying declaration. + const std::string getName() const { return Decl->getNameAsString(); } /// Checks if the record is a union. bool isUnion() const { return getDecl()->isUnion(); } /// Returns the size of the record. @@ -59,13 +63,20 @@ public: const Base *getBase(const RecordDecl *FD) const; /// Returns a virtual base descriptor. const Base *getVirtualBase(const RecordDecl *RD) const; + // Returns the destructor of the record, if any. + const CXXDestructorDecl *getDestructor() const { + if (const auto *CXXDecl = dyn_cast<CXXRecordDecl>(Decl)) + return CXXDecl->getDestructor(); + return nullptr; + } using const_field_iter = FieldList::const_iterator; llvm::iterator_range<const_field_iter> fields() const { return llvm::make_range(Fields.begin(), Fields.end()); } - unsigned getNumFields() { return Fields.size(); } + unsigned getNumFields() const { return Fields.size(); } + const Field *getField(unsigned I) const { return &Fields[I]; } Field *getField(unsigned I) { return &Fields[I]; } using const_base_iter = BaseList::const_iterator; @@ -73,7 +84,7 @@ public: return llvm::make_range(Bases.begin(), Bases.end()); } - unsigned getNumBases() { return Bases.size(); } + unsigned getNumBases() const { return Bases.size(); } Base *getBase(unsigned I) { return &Bases[I]; } using const_virtual_iter = VirtualBaseList::const_iterator; @@ -81,7 +92,7 @@ public: return llvm::make_range(VirtualBases.begin(), VirtualBases.end()); } - unsigned getNumVirtualBases() { return VirtualBases.size(); } + unsigned getNumVirtualBases() const { return VirtualBases.size(); } Base *getVirtualBase(unsigned I) { return &VirtualBases[I]; } private: @@ -108,7 +119,6 @@ private: llvm::DenseMap<const FieldDecl *, Field *> FieldMap; /// Mapping from declarations to virtual bases. llvm::DenseMap<const RecordDecl *, Base *> VirtualBaseMap; - /// Mapping from /// Size of the structure. unsigned BaseSize; /// Size of all virtual bases. diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Source.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Source.cpp index 4bec87812638..467cde116843 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Source.cpp +++ b/contrib/llvm-project/clang/lib/AST/Interp/Source.cpp @@ -28,12 +28,12 @@ const Expr *SourceInfo::asExpr() const { return nullptr; } -const Expr *SourceMapper::getExpr(Function *F, CodePtr PC) const { +const Expr *SourceMapper::getExpr(const Function *F, CodePtr PC) const { if (const Expr *E = getSource(F, PC).asExpr()) return E; llvm::report_fatal_error("missing source expression"); } -SourceLocation SourceMapper::getLocation(Function *F, CodePtr PC) const { +SourceLocation SourceMapper::getLocation(const Function *F, CodePtr PC) const { return getSource(F, PC).getLoc(); } diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Source.h b/contrib/llvm-project/clang/lib/AST/Interp/Source.h index 6acaf406b47a..99ffce34c12f 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/Source.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/Source.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_AST_INTERP_SOURCE_H #define LLVM_CLANG_AST_INTERP_SOURCE_H +#include "PrimType.h" #include "clang/AST/Decl.h" #include "clang/AST/Stmt.h" #include "llvm/Support/Endian.h" @@ -22,7 +23,7 @@ namespace interp { class Function; /// Pointer into the code segment. -class CodePtr { +class CodePtr final { public: CodePtr() : Ptr(nullptr) {} @@ -43,11 +44,14 @@ public: bool operator!=(const CodePtr &RHS) const { return Ptr != RHS.Ptr; } + operator bool() const { return Ptr; } + /// Reads data and advances the pointer. template <typename T> std::enable_if_t<!std::is_pointer<T>::value, T> read() { + assert(aligned(Ptr)); using namespace llvm::support; T Value = endian::read<T, endianness::native, 1>(Ptr); - Ptr += sizeof(T); + Ptr += align(sizeof(T)); return Value; } @@ -63,7 +67,7 @@ private: }; /// Describes the statement/declaration an opcode was generated from. -class SourceInfo { +class SourceInfo final { public: SourceInfo() {} SourceInfo(const Stmt *E) : Source(E) {} @@ -89,12 +93,12 @@ public: virtual ~SourceMapper() {} /// Returns source information for a given PC in a function. - virtual SourceInfo getSource(Function *F, CodePtr PC) const = 0; + virtual SourceInfo getSource(const Function *F, CodePtr PC) const = 0; /// Returns the expression if an opcode belongs to one, null otherwise. - const Expr *getExpr(Function *F, CodePtr PC) const; + const Expr *getExpr(const Function *F, CodePtr PC) const; /// Returns the location from which an opcode originates. - SourceLocation getLocation(Function *F, CodePtr PC) const; + SourceLocation getLocation(const Function *F, CodePtr PC) const; }; } // namespace interp diff --git a/contrib/llvm-project/clang/lib/AST/Interp/State.h b/contrib/llvm-project/clang/lib/AST/Interp/State.h index d9a645a3eb3e..131fbcf3cffc 100644 --- a/contrib/llvm-project/clang/lib/AST/Interp/State.h +++ b/contrib/llvm-project/clang/lib/AST/Interp/State.h @@ -71,6 +71,7 @@ public: virtual unsigned getCallStackDepth() = 0; public: + State() : InConstantContext(false) {} // Diagnose that the evaluation could not be folded (FF => FoldFailure) OptionalDiagnostic FFDiag(SourceLocation Loc, @@ -118,6 +119,10 @@ public: const LangOptions &getLangOpts() const; + /// Whether or not we're in a context where the front end requires a + /// constant value. + bool InConstantContext; + private: void addCallStack(unsigned Limit); |