Skip to content

Commit 1d03ec1

Browse files
committed
[clang][Interp] Support blocks
1 parent 486adc5 commit 1d03ec1

File tree

12 files changed

+109
-21
lines changed

12 files changed

+109
-21
lines changed

clang/lib/AST/Interp/ByteCodeEmitter.cpp

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

198+
/// Compile an ObjC block, i.e. ^(){}, that thing.
199+
///
200+
/// 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+
231+
assert(Func);
232+
Func->setDefined(true);
233+
// We don't compile the BlockDecl code at all right now.
234+
Func->setIsFullyCompiled(true);
235+
return Func;
236+
}
237+
198238
Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) {
199239
NextLocalOffset += sizeof(Block);
200240
unsigned Location = NextLocalOffset;

clang/lib/AST/Interp/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/Interp/Compiler.cpp

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

391391
QualType PtrType = CE->getType();
392-
assert(PtrType->isPointerType());
393-
394392
const Descriptor *Desc;
395393
if (std::optional<PrimType> T = classify(PtrType->getPointeeType()))
396394
Desc = P.createDescriptor(SubExpr, *T);
@@ -2238,8 +2236,6 @@ bool Compiler<Emitter>::VisitExprWithCleanups(const ExprWithCleanups *E) {
22382236
LocalScope<Emitter> ES(this);
22392237
const Expr *SubExpr = E->getSubExpr();
22402238

2241-
assert(E->getNumObjects() == 0 && "TODO: Implement cleanups");
2242-
22432239
return this->delegate(SubExpr) && ES.destroyLocals(E);
22442240
}
22452241

@@ -2909,6 +2905,17 @@ bool Compiler<Emitter>::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
29092905
return this->emitFree(E->isArrayForm(), E);
29102906
}
29112907

2908+
template <class Emitter>
2909+
bool Compiler<Emitter>::VisitBlockExpr(const BlockExpr *E) {
2910+
const Function *Func = nullptr;
2911+
if (auto F = Compiler<ByteCodeEmitter>(Ctx, P).compileObjCBlock(E))
2912+
Func = F;
2913+
2914+
if (!Func)
2915+
return false;
2916+
return this->emitGetFnPtr(Func, E);
2917+
}
2918+
29122919
template <class Emitter>
29132920
bool Compiler<Emitter>::VisitExpressionTraitExpr(const ExpressionTraitExpr *E) {
29142921
assert(Ctx.getLangOpts().CPlusPlus);

clang/lib/AST/Interp/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/Interp/Context.cpp

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

178178
if (T->isFunctionPointerType() || T->isFunctionReferenceType() ||
179-
T->isFunctionType())
179+
T->isFunctionType() || T->isBlockPointerType())
180180
return PT_FnPtr;
181181

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

clang/lib/AST/Interp/Function.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,20 @@ Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize,
2121
llvm::DenseMap<unsigned, ParamDescriptor> &&Params,
2222
llvm::SmallVectorImpl<unsigned> &&ParamOffsets,
2323
bool HasThisPointer, bool HasRVO, bool UnevaluatedBuiltin)
24-
: P(P), Loc(F->getBeginLoc()), F(F), ArgSize(ArgSize),
24+
: P(P), Loc(F->getBeginLoc()), Source(F), ArgSize(ArgSize),
2525
ParamTypes(std::move(ParamTypes)), Params(std::move(Params)),
2626
ParamOffsets(std::move(ParamOffsets)), HasThisPointer(HasThisPointer),
2727
HasRVO(HasRVO), Variadic(F->isVariadic()),
2828
IsUnevaluatedBuiltin(UnevaluatedBuiltin) {}
2929

30+
Function::Function(Program &P, const BlockExpr *BE, unsigned ArgSize,
31+
llvm::SmallVectorImpl<PrimType> &&ParamTypes,
32+
llvm::DenseMap<unsigned, ParamDescriptor> &&Params,
33+
llvm::SmallVectorImpl<unsigned> &&ParamOffsets)
34+
: P(P), Loc(BE->getBeginLoc()), Source(BE), ArgSize(ArgSize),
35+
ParamTypes(std::move(ParamTypes)), Params(std::move(Params)),
36+
ParamOffsets(std::move(ParamOffsets)) {}
37+
3038
Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const {
3139
auto It = Params.find(Offset);
3240
assert(It != Params.end() && "Invalid parameter offset");
@@ -46,7 +54,8 @@ SourceInfo Function::getSource(CodePtr PC) const {
4654
}
4755

4856
bool Function::isVirtual() const {
49-
if (const auto *M = dyn_cast<CXXMethodDecl>(F))
57+
if (const auto *M = dyn_cast_if_present<CXXMethodDecl>(
58+
Source.dyn_cast<const FunctionDecl *>()))
5059
return M->isVirtual();
5160
return false;
5261
}

clang/lib/AST/Interp/Function.h

Lines changed: 36 additions & 12 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 {
@@ -89,15 +90,20 @@ class Function final {
8990
CodePtr getCodeEnd() const { return Code.data() + Code.size(); }
9091

9192
/// Returns the original FunctionDecl.
92-
const FunctionDecl *getDecl() const { return F; }
93+
const FunctionDecl *getDecl() const {
94+
return Source.dyn_cast<const FunctionDecl *>();
95+
}
96+
const BlockExpr *getExpr() const {
97+
return Source.dyn_cast<const BlockExpr *>();
98+
}
9399

94100
/// Returns the name of the function decl this code
95101
/// was generated for.
96102
const std::string getName() const {
97-
if (!F)
103+
if (!Source)
98104
return "<<expr>>";
99105

100-
return F->getQualifiedNameAsString();
106+
return Source.get<const FunctionDecl *>()->getQualifiedNameAsString();
101107
}
102108

103109
/// Returns the location.
@@ -138,29 +144,38 @@ class Function final {
138144
bool isVirtual() const;
139145

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

145157
/// Returns the parent record decl, if any.
146158
const CXXRecordDecl *getParentDecl() const {
147-
if (const auto *MD = dyn_cast<CXXMethodDecl>(F))
159+
if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(
160+
Source.dyn_cast<const FunctionDecl *>()))
148161
return MD->getParent();
149162
return nullptr;
150163
}
151164

152165
/// Returns whether this function is a lambda static invoker,
153166
/// which we generate custom byte code for.
154167
bool isLambdaStaticInvoker() const {
155-
if (const auto *MD = dyn_cast<CXXMethodDecl>(F))
168+
if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(
169+
Source.dyn_cast<const FunctionDecl *>()))
156170
return MD->isLambdaStaticInvoker();
157171
return false;
158172
}
159173

160174
/// Returns whether this function is the call operator
161175
/// of a lambda record decl.
162176
bool isLambdaCallOperator() const {
163-
if (const auto *MD = dyn_cast<CXXMethodDecl>(F))
177+
if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(
178+
Source.dyn_cast<const FunctionDecl *>()))
164179
return clang::isLambdaCallOperator(MD);
165180
return false;
166181
}
@@ -178,9 +193,13 @@ class Function final {
178193

179194
bool isVariadic() const { return Variadic; }
180195

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

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

185204
bool isUnevaluatedBuiltin() const { return IsUnevaluatedBuiltin; }
186205

@@ -197,7 +216,8 @@ class Function final {
197216
}
198217

199218
bool isThisPointerExplicit() const {
200-
if (const auto *MD = dyn_cast<CXXMethodDecl>(F))
219+
if (const auto *MD = dyn_cast_if_present<CXXMethodDecl>(
220+
Source.dyn_cast<const FunctionDecl *>()))
201221
return MD->isExplicitObjectMemberFunction();
202222
return false;
203223
}
@@ -213,6 +233,10 @@ class Function final {
213233
llvm::DenseMap<unsigned, ParamDescriptor> &&Params,
214234
llvm::SmallVectorImpl<unsigned> &&ParamOffsets, bool HasThisPointer,
215235
bool HasRVO, bool UnevaluatedBuiltin);
236+
Function(Program &P, const BlockExpr *BE, unsigned ArgSize,
237+
llvm::SmallVectorImpl<PrimType> &&ParamTypes,
238+
llvm::DenseMap<unsigned, ParamDescriptor> &&Params,
239+
llvm::SmallVectorImpl<unsigned> &&ParamOffsets);
216240

217241
/// Sets the code of a function.
218242
void setCode(unsigned NewFrameSize, std::vector<std::byte> &&NewCode,
@@ -238,7 +262,7 @@ class Function final {
238262
/// Location of the executed code.
239263
SourceLocation Loc;
240264
/// Declaration this function was compiled from.
241-
const FunctionDecl *F;
265+
llvm::PointerUnion<const FunctionDecl *, const BlockExpr *> Source;
242266
/// Local area size: storage + metadata.
243267
unsigned FrameSize = 0;
244268
/// Size of the argument stack.

clang/lib/AST/Interp/FunctionPointer.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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/Interp/Interp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2708,7 +2708,7 @@ inline bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
27082708
return false;
27092709
}
27102710

2711-
if (!FuncPtr.isValid())
2711+
if (!FuncPtr.isValid() || !F->getDecl())
27122712
return Invalid(S, OpPC);
27132713

27142714
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)