[clang] [clang][Interp] Support ObjC blocks (PR #104551)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 16 08:15:15 PDT 2024


https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/104551

>From e4e5a35bf25f8f95ea39765692d0280b906dd625 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 15 Aug 2024 20:31:38 +0200
Subject: [PATCH] [clang][Interp] Support blocks

---
 clang/lib/AST/ByteCode/ByteCodeEmitter.cpp | 42 +++++++++++++++++++
 clang/lib/AST/ByteCode/ByteCodeEmitter.h   |  1 +
 clang/lib/AST/ByteCode/Compiler.cpp        | 15 +++++--
 clang/lib/AST/ByteCode/Compiler.h          |  1 +
 clang/lib/AST/ByteCode/Context.cpp         |  2 +-
 clang/lib/AST/ByteCode/Function.cpp        | 14 ++++---
 clang/lib/AST/ByteCode/Function.h          | 49 ++++++++++++++++------
 clang/lib/AST/ByteCode/FunctionPointer.h   |  7 +++-
 clang/lib/AST/ByteCode/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/ByteCode/ByteCodeEmitter.cpp b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
index 1f6bdaff50b5bf..a605fbaadb0bf7 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
+++ b/clang/lib/AST/ByteCode/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/ByteCode/ByteCodeEmitter.h b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
index 59e12defd8e52b..7cbbe651699b34 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h
+++ b/clang/lib/AST/ByteCode/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/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index c3ecaa40b86f2a..daced2f8580a63 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/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/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 74bfce5f241f28..5acfe3c41796c4 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/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/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index e9c1fd1b8dc9f9..c26b71efbb26a6 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -179,7 +179,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/ByteCode/Function.cpp b/clang/lib/AST/ByteCode/Function.cpp
index fc3345cbe123f8..e3fab3f6720b41 100644
--- a/clang/lib/AST/ByteCode/Function.cpp
+++ b/clang/lib/AST/ByteCode/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/ByteCode/Function.h b/clang/lib/AST/ByteCode/Function.h
index 625d5af0407a72..f254db20d4f594 100644
--- a/clang/lib/AST/ByteCode/Function.h
+++ b/clang/lib/AST/ByteCode/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/ByteCode/FunctionPointer.h b/clang/lib/AST/ByteCode/FunctionPointer.h
index d92cd32933fcdc..c9bdfbee55441a 100644
--- a/clang/lib/AST/ByteCode/FunctionPointer.h
+++ b/clang/lib/AST/ByteCode/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/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index c3ce21cb13770e..40a343c27901ca 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -2709,7 +2709,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
 



More information about the cfe-commits mailing list