Skip to content

Commit ca148b2

Browse files
authored
[clang][bytecode] Support ObjC blocks (#104551)
I started out by adding a new pointer type for blocks, and I was fully prepared to compile their AST to bytecode and later call them. ... then I found out that the current interpreter doesn't support calling blocks at all. So we reuse `Function` to support sources other than `FunctionDecl`s and classify `BlockPointerType` as `PT_FnPtr`.
1 parent 06fd808 commit ca148b2

File tree

12 files changed

+110
-26
lines changed

12 files changed

+110
-26
lines changed

clang/lib/AST/ByteCode/ByteCodeEmitter.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,48 @@ Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
195195
return Func;
196196
}
197197

198+
/// Compile an ObjC block, i.e. ^(){}, that thing.
199+
///
200+
/// FIXME: We do not support calling the block though, so we create a function
201+
/// here but do not compile any code for it.
202+
Function *ByteCodeEmitter::compileObjCBlock(const BlockExpr *BE) {
203+
const BlockDecl *BD = BE->getBlockDecl();
204+
// Set up argument indices.
205+
unsigned ParamOffset = 0;
206+
SmallVector<PrimType, 8> ParamTypes;
207+
SmallVector<unsigned, 8> ParamOffsets;
208+
llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors;
209+
210+
// Assign descriptors to all parameters.
211+
// Composite objects are lowered to pointers.
212+
for (const ParmVarDecl *PD : BD->parameters()) {
213+
std::optional<PrimType> T = Ctx.classify(PD->getType());
214+
PrimType PT = T.value_or(PT_Ptr);
215+
Descriptor *Desc = P.createDescriptor(PD, PT);
216+
ParamDescriptors.insert({ParamOffset, {PT, Desc}});
217+
Params.insert({PD, {ParamOffset, T != std::nullopt}});
218+
ParamOffsets.push_back(ParamOffset);
219+
ParamOffset += align(primSize(PT));
220+
ParamTypes.push_back(PT);
221+
}
222+
223+
if (BD->hasCaptures())
224+
return nullptr;
225+
226+
// Create a handle over the emitted code.
227+
Function *Func =
228+
P.createFunction(BE, ParamOffset, std::move(ParamTypes),
229+
std::move(ParamDescriptors), std::move(ParamOffsets),
230+
/*HasThisPointer=*/false, /*HasRVO=*/false,
231+
/*IsUnevaluatedBuiltin=*/false);
232+
233+
assert(Func);
234+
Func->setDefined(true);
235+
// We don't compile the BlockDecl code at all right now.
236+
Func->setIsFullyCompiled(true);
237+
return Func;
238+
}
239+
198240
Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) {
199241
NextLocalOffset += sizeof(Block);
200242
unsigned Location = NextLocalOffset;

clang/lib/AST/ByteCode/ByteCodeEmitter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class ByteCodeEmitter {
3232
public:
3333
/// Compiles the function into the module.
3434
Function *compileFunc(const FunctionDecl *FuncDecl);
35+
Function *compileObjCBlock(const BlockExpr *BE);
3536

3637
protected:
3738
ByteCodeEmitter(Context &Ctx, Program &P) : Ctx(Ctx), P(P) {}

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,8 +391,6 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
391391
return this->emitPop(T, CE);
392392

393393
QualType PtrType = CE->getType();
394-
assert(PtrType->isPointerType());
395-
396394
const Descriptor *Desc;
397395
if (std::optional<PrimType> T = classify(PtrType->getPointeeType()))
398396
Desc = P.createDescriptor(SubExpr, *T);
@@ -2242,8 +2240,6 @@ bool Compiler<Emitter>::VisitExprWithCleanups(const ExprWithCleanups *E) {
22422240
LocalScope<Emitter> ES(this);
22432241
const Expr *SubExpr = E->getSubExpr();
22442242

2245-
assert(E->getNumObjects() == 0 && "TODO: Implement cleanups");
2246-
22472243
return this->delegate(SubExpr) && ES.destroyLocals(E);
22482244
}
22492245

@@ -2913,6 +2909,17 @@ bool Compiler<Emitter>::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
29132909
return this->emitFree(E->isArrayForm(), E);
29142910
}
29152911

2912+
template <class Emitter>
2913+
bool Compiler<Emitter>::VisitBlockExpr(const BlockExpr *E) {
2914+
const Function *Func = nullptr;
2915+
if (auto F = Compiler<ByteCodeEmitter>(Ctx, P).compileObjCBlock(E))
2916+
Func = F;
2917+
2918+
if (!Func)
2919+
return false;
2920+
return this->emitGetFnPtr(Func, E);
2921+
}
2922+
29162923
template <class Emitter>
29172924
bool Compiler<Emitter>::VisitExpressionTraitExpr(const ExpressionTraitExpr *E) {
29182925
assert(Ctx.getLangOpts().CPlusPlus);

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
199199
bool VisitStmtExpr(const StmtExpr *E);
200200
bool VisitCXXNewExpr(const CXXNewExpr *E);
201201
bool VisitCXXDeleteExpr(const CXXDeleteExpr *E);
202+
bool VisitBlockExpr(const BlockExpr *E);
202203

203204
// Statements.
204205
bool visitCompoundStmt(const CompoundStmt *S);

clang/lib/AST/ByteCode/Context.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ std::optional<PrimType> Context::classify(QualType T) const {
179179
return PT_MemberPtr;
180180

181181
if (T->isFunctionPointerType() || T->isFunctionReferenceType() ||
182-
T->isFunctionType())
182+
T->isFunctionType() || T->isBlockPointerType())
183183
return PT_FnPtr;
184184

185185
if (T->isPointerOrReferenceType() || T->isObjCObjectPointerType())

clang/lib/AST/ByteCode/Function.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@
1616
using namespace clang;
1717
using namespace clang::interp;
1818

19-
Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize,
19+
Function::Function(Program &P, FunctionDeclTy Source, unsigned ArgSize,
2020
llvm::SmallVectorImpl<PrimType> &&ParamTypes,
2121
llvm::DenseMap<unsigned, ParamDescriptor> &&Params,
2222
llvm::SmallVectorImpl<unsigned> &&ParamOffsets,
2323
bool HasThisPointer, bool HasRVO, bool UnevaluatedBuiltin)
24-
: P(P), F(F), ArgSize(ArgSize), ParamTypes(std::move(ParamTypes)),
24+
: P(P), Source(Source), ArgSize(ArgSize), ParamTypes(std::move(ParamTypes)),
2525
Params(std::move(Params)), ParamOffsets(std::move(ParamOffsets)),
26-
HasThisPointer(HasThisPointer), HasRVO(HasRVO), Variadic(F->isVariadic()),
27-
IsUnevaluatedBuiltin(UnevaluatedBuiltin) {}
26+
HasThisPointer(HasThisPointer), HasRVO(HasRVO),
27+
IsUnevaluatedBuiltin(UnevaluatedBuiltin) {
28+
if (const auto *F = Source.dyn_cast<const FunctionDecl *>())
29+
Variadic = F->isVariadic();
30+
}
2831

2932
Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const {
3033
auto It = Params.find(Offset);
@@ -45,7 +48,8 @@ SourceInfo Function::getSource(CodePtr PC) const {
4548
}
4649

4750
bool Function::isVirtual() const {
48-
if (const auto *M = dyn_cast<CXXMethodDecl>(F))
51+
if (const auto *M = dyn_cast_if_present<CXXMethodDecl>(
52+
Source.dyn_cast<const FunctionDecl *>()))
4953
return M->isVirtual();
5054
return false;
5155
}

clang/lib/AST/ByteCode/Function.h

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "clang/AST/ASTLambda.h"
2121
#include "clang/AST/Attr.h"
2222
#include "clang/AST/Decl.h"
23+
#include "llvm/ADT/PointerUnion.h"
2324
#include "llvm/Support/raw_ostream.h"
2425

2526
namespace clang {
@@ -55,6 +56,9 @@ class Scope final {
5556
LocalVectorTy Descriptors;
5657
};
5758

59+
using FunctionDeclTy =
60+
llvm::PointerUnion<const FunctionDecl *, const BlockExpr *>;
61+
5862
/// Bytecode function.
5963
///
6064
/// Contains links to the bytecode of the function, as well as metadata
@@ -89,15 +93,20 @@ class Function final {
8993
CodePtr getCodeEnd() const { return Code.data() + Code.size(); }
9094

9195
/// Returns the original FunctionDecl.
92-
const FunctionDecl *getDecl() const { return F; }
96+
const FunctionDecl *getDecl() const {
97+
return Source.dyn_cast<const FunctionDecl *>();
98+
}
99+
const BlockExpr *getExpr() const {
100+
return Source.dyn_cast<const BlockExpr *>();
101+
}
93102

94103
/// Returns the name of the function decl this code
95104
/// was generated for.
96105
const std::string getName() const {
97-
if (!F)
106+
if (!Source)
98107
return "<<expr>>";
99108

100-
return F->getQualifiedNameAsString();
109+
return Source.get<const FunctionDecl *>()->getQualifiedNameAsString();
101110
}
102111

103112
/// Returns a parameter descriptor.
@@ -135,29 +144,38 @@ class Function final {
135144
bool isVirtual() const;
136145

137146
/// Checks if the function is a constructor.
138-
bool isConstructor() const { return isa<CXXConstructorDecl>(F); }
147+
bool isConstructor() const {
148+
return isa_and_nonnull<CXXConstructorDecl>(
149+
Source.dyn_cast<const FunctionDecl *>());
150+
}
139151
/// Checks if the function is a destructor.
140-
bool isDestructor() const { return isa<CXXDestructorDecl>(F); }
152+
bool isDestructor() const {
153+
return isa_and_nonnull<CXXDestructorDecl>(
154+
Source.dyn_cast<const FunctionDecl *>());
155+
}
141156

142157
/// Returns the parent record decl, if any.
143158
const CXXRecordDecl *getParentDecl() const {
144-
if (const auto *MD = dyn_cast<CXXMethodDecl>(F))
159+
if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(
160+
Source.dyn_cast<const FunctionDecl *>()))
145161
return MD->getParent();
146162
return nullptr;
147163
}
148164

149165
/// Returns whether this function is a lambda static invoker,
150166
/// which we generate custom byte code for.
151167
bool isLambdaStaticInvoker() const {
152-
if (const auto *MD = dyn_cast<CXXMethodDecl>(F))
168+
if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(
169+
Source.dyn_cast<const FunctionDecl *>()))
153170
return MD->isLambdaStaticInvoker();
154171
return false;
155172
}
156173

157174
/// Returns whether this function is the call operator
158175
/// of a lambda record decl.
159176
bool isLambdaCallOperator() const {
160-
if (const auto *MD = dyn_cast<CXXMethodDecl>(F))
177+
if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(
178+
Source.dyn_cast<const FunctionDecl *>()))
161179
return clang::isLambdaCallOperator(MD);
162180
return false;
163181
}
@@ -175,9 +193,13 @@ class Function final {
175193

176194
bool isVariadic() const { return Variadic; }
177195

178-
unsigned getBuiltinID() const { return F->getBuiltinID(); }
196+
unsigned getBuiltinID() const {
197+
return Source.get<const FunctionDecl *>()->getBuiltinID();
198+
}
179199

180-
bool isBuiltin() const { return F->getBuiltinID() != 0; }
200+
bool isBuiltin() const {
201+
return Source.get<const FunctionDecl *>()->getBuiltinID() != 0;
202+
}
181203

182204
bool isUnevaluatedBuiltin() const { return IsUnevaluatedBuiltin; }
183205

@@ -194,7 +216,8 @@ class Function final {
194216
}
195217

196218
bool isThisPointerExplicit() const {
197-
if (const auto *MD = dyn_cast<CXXMethodDecl>(F))
219+
if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(
220+
Source.dyn_cast<const FunctionDecl *>()))
198221
return MD->isExplicitObjectMemberFunction();
199222
return false;
200223
}
@@ -205,7 +228,7 @@ class Function final {
205228

206229
private:
207230
/// Construct a function representing an actual function.
208-
Function(Program &P, const FunctionDecl *F, unsigned ArgSize,
231+
Function(Program &P, FunctionDeclTy Source, unsigned ArgSize,
209232
llvm::SmallVectorImpl<PrimType> &&ParamTypes,
210233
llvm::DenseMap<unsigned, ParamDescriptor> &&Params,
211234
llvm::SmallVectorImpl<unsigned> &&ParamOffsets, bool HasThisPointer,
@@ -233,7 +256,7 @@ class Function final {
233256
/// Program reference.
234257
Program &P;
235258
/// Declaration this function was compiled from.
236-
const FunctionDecl *F;
259+
FunctionDeclTy Source;
237260
/// Local area size: storage + metadata.
238261
unsigned FrameSize = 0;
239262
/// Size of the argument stack.

clang/lib/AST/ByteCode/FunctionPointer.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class FunctionPointer final {
3333
bool isZero() const { return !Func; }
3434
bool isValid() const { return Valid; }
3535
bool isWeak() const {
36-
if (!Func || !Valid)
36+
if (!Func || !Valid || !Func->getDecl())
3737
return false;
3838

3939
return Func->getDecl()->isWeak();
@@ -49,7 +49,10 @@ class FunctionPointer final {
4949
CharUnits::fromQuantity(getIntegerRepresentation()), {},
5050
/*OnePastTheEnd=*/false, /*IsNull=*/false);
5151

52-
return APValue(Func->getDecl(), CharUnits::Zero(), {},
52+
if (Func->getDecl())
53+
return APValue(Func->getDecl(), CharUnits::Zero(), {},
54+
/*OnePastTheEnd=*/false, /*IsNull=*/false);
55+
return APValue(Func->getExpr(), CharUnits::Zero(), {},
5356
/*OnePastTheEnd=*/false, /*IsNull=*/false);
5457
}
5558

clang/lib/AST/ByteCode/Interp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2725,7 +2725,7 @@ inline bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
27252725
return false;
27262726
}
27272727

2728-
if (!FuncPtr.isValid())
2728+
if (!FuncPtr.isValid() || !F->getDecl())
27292729
return Invalid(S, OpPC);
27302730

27312731
assert(F);

clang/test/Sema/block-misc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -verify %s -fblocks
2+
// RUN: %clang_cc1 -fsyntax-only -Wno-strict-prototypes -verify %s -fblocks -fexperimental-new-constant-interpreter
23
void donotwarn(void);
34

45
int (^IFP) ();

clang/test/Sema/block-return.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast -pedantic -fsyntax-only %s -verify -fblocks
2+
// RUN: %clang_cc1 -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast -pedantic -fsyntax-only %s -verify -fblocks -fexperimental-new-constant-interpreter
23

34
extern int printf(const char *, ...);
45

clang/test/SemaCXX/consteval-cleanup.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 -fblocks -Wno-unused-value -std=c++20 -ast-dump -verify %s -ast-dump | FileCheck %s
2+
// RUN: %clang_cc1 -fblocks -Wno-unused-value -std=c++20 -ast-dump -verify %s -ast-dump -fexperimental-new-constant-interpreter | FileCheck %s
23

34
// expected-no-diagnostics
45

0 commit comments

Comments
 (0)