https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/104551
>From 24a00c98538038c5fd7f01789b8b819c3c562a46 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 | 42 ++++++++++++++++++++ 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 | 14 ++++--- clang/lib/AST/Interp/Function.h | 49 +++++++++++++++++------- clang/lib/AST/Interp/FunctionPointer.h | 7 +++- 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, 110 insertions(+), 26 deletions(-) diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp index a01fa15dc0b7dc..9aec46767eb822 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -195,6 +195,48 @@ 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), + /*HasThisPointer=*/false, /*HasRVO=*/false, + /*IsUnevaluatedBuiltin=*/false); + + 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 5e1f507ca2b178..ece4b039df8e54 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); @@ -2240,8 +2238,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); } @@ -2911,6 +2907,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 74bfce5f241f28..5acfe3c41796c4 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 fc3345cbe123f8..e3fab3f6720b41 100644 --- a/clang/lib/AST/Interp/Function.cpp +++ b/clang/lib/AST/Interp/Function.cpp @@ -16,15 +16,18 @@ using namespace clang; using namespace clang::interp; -Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize, +Function::Function(Program &P, FunctionDeclTy Source, unsigned ArgSize, llvm::SmallVectorImpl<PrimType> &&ParamTypes, llvm::DenseMap<unsigned, ParamDescriptor> &&Params, llvm::SmallVectorImpl<unsigned> &&ParamOffsets, bool HasThisPointer, bool HasRVO, bool UnevaluatedBuiltin) - : P(P), F(F), ArgSize(ArgSize), ParamTypes(std::move(ParamTypes)), + : P(P), Source(Source), ArgSize(ArgSize), ParamTypes(std::move(ParamTypes)), Params(std::move(Params)), ParamOffsets(std::move(ParamOffsets)), - HasThisPointer(HasThisPointer), HasRVO(HasRVO), Variadic(F->isVariadic()), - IsUnevaluatedBuiltin(UnevaluatedBuiltin) {} + HasThisPointer(HasThisPointer), HasRVO(HasRVO), + IsUnevaluatedBuiltin(UnevaluatedBuiltin) { + if (const auto *F = Source.dyn_cast<const FunctionDecl *>()) + Variadic = F->isVariadic(); +} Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const { auto It = Params.find(Offset); @@ -45,7 +48,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 625d5af0407a72..f254db20d4f594 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 { @@ -55,6 +56,9 @@ class Scope final { LocalVectorTy Descriptors; }; +using FunctionDeclTy = + llvm::PointerUnion<const FunctionDecl *, const BlockExpr *>; + /// Bytecode function. /// /// Contains links to the bytecode of the function, as well as metadata @@ -89,15 +93,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 a parameter descriptor. @@ -135,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; } @@ -149,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; } @@ -157,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; } @@ -175,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; } @@ -194,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; } @@ -205,7 +228,7 @@ class Function final { private: /// Construct a function representing an actual function. - Function(Program &P, const FunctionDecl *F, unsigned ArgSize, + Function(Program &P, FunctionDeclTy Source, unsigned ArgSize, llvm::SmallVectorImpl<PrimType> &&ParamTypes, llvm::DenseMap<unsigned, ParamDescriptor> &&Params, llvm::SmallVectorImpl<unsigned> &&ParamOffsets, bool HasThisPointer, @@ -233,7 +256,7 @@ class Function final { /// Program reference. Program &P; /// Declaration this function was compiled from. - const FunctionDecl *F; + FunctionDeclTy 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..c9bdfbee55441a 100644 --- a/clang/lib/AST/Interp/FunctionPointer.h +++ b/clang/lib/AST/Interp/FunctionPointer.h @@ -33,7 +33,7 @@ class FunctionPointer final { bool isZero() const { return !Func; } bool isValid() const { return Valid; } bool isWeak() const { - if (!Func || !Valid) + if (!Func || !Valid || !Func->getDecl()) return false; return Func->getDecl()->isWeak(); @@ -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 c2d73f32f0b20c..0e0c9f1edfc642 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