https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/104551
>From 1d03ec1fd0066b8e73c1ae969b9235936ba7ae7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Thu, 15 Aug 2024 20:31:38 +0200 Subject: [PATCH] [clang][Interp] Support blocks --- clang/lib/AST/Interp/ByteCodeEmitter.cpp | 40 ++++++++++++++++++++ clang/lib/AST/Interp/ByteCodeEmitter.h | 1 + clang/lib/AST/Interp/Compiler.cpp | 15 ++++++-- clang/lib/AST/Interp/Compiler.h | 1 + clang/lib/AST/Interp/Context.cpp | 2 +- clang/lib/AST/Interp/Function.cpp | 13 ++++++- clang/lib/AST/Interp/Function.h | 48 ++++++++++++++++++------ clang/lib/AST/Interp/FunctionPointer.h | 5 ++- clang/lib/AST/Interp/Interp.h | 2 +- clang/test/Sema/block-misc.c | 1 + clang/test/Sema/block-return.c | 1 + clang/test/SemaCXX/consteval-cleanup.cpp | 1 + 12 files changed, 109 insertions(+), 21 deletions(-) diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp index a01fa15dc0b7dc..8aab8eb42ea816 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -195,6 +195,46 @@ Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) { return Func; } +/// Compile an ObjC block, i.e. ^(){}, that thing. +/// +/// We do not support calling the block though, so we create a function +/// here but do not compile any code for it. +Function *ByteCodeEmitter::compileObjCBlock(const BlockExpr *BE) { + const BlockDecl *BD = BE->getBlockDecl(); + // Set up argument indices. + unsigned ParamOffset = 0; + SmallVector<PrimType, 8> ParamTypes; + SmallVector<unsigned, 8> ParamOffsets; + llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors; + + // Assign descriptors to all parameters. + // Composite objects are lowered to pointers. + for (const ParmVarDecl *PD : BD->parameters()) { + std::optional<PrimType> T = Ctx.classify(PD->getType()); + PrimType PT = T.value_or(PT_Ptr); + Descriptor *Desc = P.createDescriptor(PD, PT); + ParamDescriptors.insert({ParamOffset, {PT, Desc}}); + Params.insert({PD, {ParamOffset, T != std::nullopt}}); + ParamOffsets.push_back(ParamOffset); + ParamOffset += align(primSize(PT)); + ParamTypes.push_back(PT); + } + + if (BD->hasCaptures()) + return nullptr; + + // Create a handle over the emitted code. + Function *Func = + P.createFunction(BE, ParamOffset, std::move(ParamTypes), + std::move(ParamDescriptors), std::move(ParamOffsets)); + + assert(Func); + Func->setDefined(true); + // We don't compile the BlockDecl code at all right now. + Func->setIsFullyCompiled(true); + return Func; +} + Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) { NextLocalOffset += sizeof(Block); unsigned Location = NextLocalOffset; diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.h b/clang/lib/AST/Interp/ByteCodeEmitter.h index a19a25c2f9e8ec..915960cb515ce6 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.h +++ b/clang/lib/AST/Interp/ByteCodeEmitter.h @@ -32,6 +32,7 @@ class ByteCodeEmitter { public: /// Compiles the function into the module. Function *compileFunc(const FunctionDecl *FuncDecl); + Function *compileObjCBlock(const BlockExpr *BE); protected: ByteCodeEmitter(Context &Ctx, Program &P) : Ctx(Ctx), P(P) {} diff --git a/clang/lib/AST/Interp/Compiler.cpp b/clang/lib/AST/Interp/Compiler.cpp index d32b595f9f0724..109dbaf7cdf0eb 100644 --- a/clang/lib/AST/Interp/Compiler.cpp +++ b/clang/lib/AST/Interp/Compiler.cpp @@ -389,8 +389,6 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) { return this->emitPop(T, CE); QualType PtrType = CE->getType(); - assert(PtrType->isPointerType()); - const Descriptor *Desc; if (std::optional<PrimType> T = classify(PtrType->getPointeeType())) Desc = P.createDescriptor(SubExpr, *T); @@ -2238,8 +2236,6 @@ bool Compiler<Emitter>::VisitExprWithCleanups(const ExprWithCleanups *E) { LocalScope<Emitter> ES(this); const Expr *SubExpr = E->getSubExpr(); - assert(E->getNumObjects() == 0 && "TODO: Implement cleanups"); - return this->delegate(SubExpr) && ES.destroyLocals(E); } @@ -2909,6 +2905,17 @@ bool Compiler<Emitter>::VisitCXXDeleteExpr(const CXXDeleteExpr *E) { return this->emitFree(E->isArrayForm(), E); } +template <class Emitter> +bool Compiler<Emitter>::VisitBlockExpr(const BlockExpr *E) { + const Function *Func = nullptr; + if (auto F = Compiler<ByteCodeEmitter>(Ctx, P).compileObjCBlock(E)) + Func = F; + + if (!Func) + return false; + return this->emitGetFnPtr(Func, E); +} + template <class Emitter> bool Compiler<Emitter>::VisitExpressionTraitExpr(const ExpressionTraitExpr *E) { assert(Ctx.getLangOpts().CPlusPlus); diff --git a/clang/lib/AST/Interp/Compiler.h b/clang/lib/AST/Interp/Compiler.h index 112219c49e8bdd..f2a2a2aaecaab3 100644 --- a/clang/lib/AST/Interp/Compiler.h +++ b/clang/lib/AST/Interp/Compiler.h @@ -199,6 +199,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>, bool VisitStmtExpr(const StmtExpr *E); bool VisitCXXNewExpr(const CXXNewExpr *E); bool VisitCXXDeleteExpr(const CXXDeleteExpr *E); + bool VisitBlockExpr(const BlockExpr *E); // Statements. bool visitCompoundStmt(const CompoundStmt *S); diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp index 92ac28137fdb45..8b7a33de7288e7 100644 --- a/clang/lib/AST/Interp/Context.cpp +++ b/clang/lib/AST/Interp/Context.cpp @@ -176,7 +176,7 @@ std::optional<PrimType> Context::classify(QualType T) const { return PT_MemberPtr; if (T->isFunctionPointerType() || T->isFunctionReferenceType() || - T->isFunctionType()) + T->isFunctionType() || T->isBlockPointerType()) return PT_FnPtr; if (T->isPointerOrReferenceType() || T->isObjCObjectPointerType()) diff --git a/clang/lib/AST/Interp/Function.cpp b/clang/lib/AST/Interp/Function.cpp index 00f5a1fced531a..6ed251f1d9aff9 100644 --- a/clang/lib/AST/Interp/Function.cpp +++ b/clang/lib/AST/Interp/Function.cpp @@ -21,12 +21,20 @@ Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize, llvm::DenseMap<unsigned, ParamDescriptor> &&Params, llvm::SmallVectorImpl<unsigned> &&ParamOffsets, bool HasThisPointer, bool HasRVO, bool UnevaluatedBuiltin) - : P(P), Loc(F->getBeginLoc()), F(F), ArgSize(ArgSize), + : P(P), Loc(F->getBeginLoc()), Source(F), ArgSize(ArgSize), ParamTypes(std::move(ParamTypes)), Params(std::move(Params)), ParamOffsets(std::move(ParamOffsets)), HasThisPointer(HasThisPointer), HasRVO(HasRVO), Variadic(F->isVariadic()), IsUnevaluatedBuiltin(UnevaluatedBuiltin) {} +Function::Function(Program &P, const BlockExpr *BE, unsigned ArgSize, + llvm::SmallVectorImpl<PrimType> &&ParamTypes, + llvm::DenseMap<unsigned, ParamDescriptor> &&Params, + llvm::SmallVectorImpl<unsigned> &&ParamOffsets) + : P(P), Loc(BE->getBeginLoc()), Source(BE), ArgSize(ArgSize), + ParamTypes(std::move(ParamTypes)), Params(std::move(Params)), + ParamOffsets(std::move(ParamOffsets)) {} + Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const { auto It = Params.find(Offset); assert(It != Params.end() && "Invalid parameter offset"); @@ -46,7 +54,8 @@ SourceInfo Function::getSource(CodePtr PC) const { } bool Function::isVirtual() const { - if (const auto *M = dyn_cast<CXXMethodDecl>(F)) + if (const auto *M = dyn_cast_if_present<CXXMethodDecl>( + Source.dyn_cast<const FunctionDecl *>())) return M->isVirtual(); return false; } diff --git a/clang/lib/AST/Interp/Function.h b/clang/lib/AST/Interp/Function.h index 92bcd96927912d..130c1ecf7a2b32 100644 --- a/clang/lib/AST/Interp/Function.h +++ b/clang/lib/AST/Interp/Function.h @@ -20,6 +20,7 @@ #include "clang/AST/ASTLambda.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" +#include "llvm/ADT/PointerUnion.h" #include "llvm/Support/raw_ostream.h" namespace clang { @@ -89,15 +90,20 @@ class Function final { CodePtr getCodeEnd() const { return Code.data() + Code.size(); } /// Returns the original FunctionDecl. - const FunctionDecl *getDecl() const { return F; } + const FunctionDecl *getDecl() const { + return Source.dyn_cast<const FunctionDecl *>(); + } + const BlockExpr *getExpr() const { + return Source.dyn_cast<const BlockExpr *>(); + } /// Returns the name of the function decl this code /// was generated for. const std::string getName() const { - if (!F) + if (!Source) return "<<expr>>"; - return F->getQualifiedNameAsString(); + return Source.get<const FunctionDecl *>()->getQualifiedNameAsString(); } /// Returns the location. @@ -138,13 +144,20 @@ class Function final { bool isVirtual() const; /// Checks if the function is a constructor. - bool isConstructor() const { return isa<CXXConstructorDecl>(F); } + bool isConstructor() const { + return isa_and_nonnull<CXXConstructorDecl>( + Source.dyn_cast<const FunctionDecl *>()); + } /// Checks if the function is a destructor. - bool isDestructor() const { return isa<CXXDestructorDecl>(F); } + bool isDestructor() const { + return isa_and_nonnull<CXXDestructorDecl>( + Source.dyn_cast<const FunctionDecl *>()); + } /// Returns the parent record decl, if any. const CXXRecordDecl *getParentDecl() const { - if (const auto *MD = dyn_cast<CXXMethodDecl>(F)) + if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>( + Source.dyn_cast<const FunctionDecl *>())) return MD->getParent(); return nullptr; } @@ -152,7 +165,8 @@ class Function final { /// Returns whether this function is a lambda static invoker, /// which we generate custom byte code for. bool isLambdaStaticInvoker() const { - if (const auto *MD = dyn_cast<CXXMethodDecl>(F)) + if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>( + Source.dyn_cast<const FunctionDecl *>())) return MD->isLambdaStaticInvoker(); return false; } @@ -160,7 +174,8 @@ class Function final { /// Returns whether this function is the call operator /// of a lambda record decl. bool isLambdaCallOperator() const { - if (const auto *MD = dyn_cast<CXXMethodDecl>(F)) + if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>( + Source.dyn_cast<const FunctionDecl *>())) return clang::isLambdaCallOperator(MD); return false; } @@ -178,9 +193,13 @@ class Function final { bool isVariadic() const { return Variadic; } - unsigned getBuiltinID() const { return F->getBuiltinID(); } + unsigned getBuiltinID() const { + return Source.get<const FunctionDecl *>()->getBuiltinID(); + } - bool isBuiltin() const { return F->getBuiltinID() != 0; } + bool isBuiltin() const { + return Source.get<const FunctionDecl *>()->getBuiltinID() != 0; + } bool isUnevaluatedBuiltin() const { return IsUnevaluatedBuiltin; } @@ -197,7 +216,8 @@ class Function final { } bool isThisPointerExplicit() const { - if (const auto *MD = dyn_cast<CXXMethodDecl>(F)) + if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>( + Source.dyn_cast<const FunctionDecl *>())) return MD->isExplicitObjectMemberFunction(); return false; } @@ -213,6 +233,10 @@ class Function final { llvm::DenseMap<unsigned, ParamDescriptor> &&Params, llvm::SmallVectorImpl<unsigned> &&ParamOffsets, bool HasThisPointer, bool HasRVO, bool UnevaluatedBuiltin); + Function(Program &P, const BlockExpr *BE, unsigned ArgSize, + llvm::SmallVectorImpl<PrimType> &&ParamTypes, + llvm::DenseMap<unsigned, ParamDescriptor> &&Params, + llvm::SmallVectorImpl<unsigned> &&ParamOffsets); /// Sets the code of a function. void setCode(unsigned NewFrameSize, std::vector<std::byte> &&NewCode, @@ -238,7 +262,7 @@ class Function final { /// Location of the executed code. SourceLocation Loc; /// Declaration this function was compiled from. - const FunctionDecl *F; + llvm::PointerUnion<const FunctionDecl *, const BlockExpr *> Source; /// Local area size: storage + metadata. unsigned FrameSize = 0; /// Size of the argument stack. diff --git a/clang/lib/AST/Interp/FunctionPointer.h b/clang/lib/AST/Interp/FunctionPointer.h index d92cd32933fcdc..23df0e74a651f2 100644 --- a/clang/lib/AST/Interp/FunctionPointer.h +++ b/clang/lib/AST/Interp/FunctionPointer.h @@ -49,7 +49,10 @@ class FunctionPointer final { CharUnits::fromQuantity(getIntegerRepresentation()), {}, /*OnePastTheEnd=*/false, /*IsNull=*/false); - return APValue(Func->getDecl(), CharUnits::Zero(), {}, + if (Func->getDecl()) + return APValue(Func->getDecl(), CharUnits::Zero(), {}, + /*OnePastTheEnd=*/false, /*IsNull=*/false); + return APValue(Func->getExpr(), CharUnits::Zero(), {}, /*OnePastTheEnd=*/false, /*IsNull=*/false); } diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 3eab0cfd871385..446fe6b01d8a87 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -2708,7 +2708,7 @@ inline bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize, return false; } - if (!FuncPtr.isValid()) + if (!FuncPtr.isValid() || !F->getDecl()) return Invalid(S, OpPC); assert(F); diff --git a/clang/test/Sema/block-misc.c b/clang/test/Sema/block-misc.c index aea44d55a606a4..c8a34b7f3c9fd5 100644 --- a/clang/test/Sema/block-misc.c +++ b/clang/test/Sema/block-misc.c @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -verify %s -fblocks +// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -verify %s -fblocks -fexperimental-new-constant-interpreter void donotwarn(void); int (^IFP) (); diff --git a/clang/test/Sema/block-return.c b/clang/test/Sema/block-return.c index d3d70511b18513..126fc6f953dea2 100644 --- a/clang/test/Sema/block-return.c +++ b/clang/test/Sema/block-return.c @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast -pedantic -fsyntax-only %s -verify -fblocks +// RUN: %clang_cc1 -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast -pedantic -fsyntax-only %s -verify -fblocks -fexperimental-new-constant-interpreter extern int printf(const char *, ...); diff --git a/clang/test/SemaCXX/consteval-cleanup.cpp b/clang/test/SemaCXX/consteval-cleanup.cpp index 499c45db501770..f7d033b2ecafa4 100644 --- a/clang/test/SemaCXX/consteval-cleanup.cpp +++ b/clang/test/SemaCXX/consteval-cleanup.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fblocks -Wno-unused-value -std=c++20 -ast-dump -verify %s -ast-dump | FileCheck %s +// RUN: %clang_cc1 -fblocks -Wno-unused-value -std=c++20 -ast-dump -verify %s -ast-dump -fexperimental-new-constant-interpreter | FileCheck %s // expected-no-diagnostics _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits