[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`.
This commit is contained in:
parent
06fd808654
commit
ca148b2150
@ -195,6 +195,48 @@ Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
|
||||
return Func;
|
||||
}
|
||||
|
||||
/// Compile an ObjC block, i.e. ^(){}, that thing.
|
||||
///
|
||||
/// FIXME: 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;
|
||||
|
||||
@ -32,6 +32,7 @@ protected:
|
||||
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) {}
|
||||
|
||||
@ -391,8 +391,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);
|
||||
@ -2242,8 +2240,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);
|
||||
}
|
||||
|
||||
@ -2913,6 +2909,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);
|
||||
|
||||
@ -199,6 +199,7 @@ public:
|
||||
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);
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 @@ private:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
/// 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 @@ public:
|
||||
/// 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 @@ public:
|
||||
|
||||
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 @@ public:
|
||||
}
|
||||
|
||||
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 @@ public:
|
||||
|
||||
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 @@ private:
|
||||
/// 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.
|
||||
|
||||
@ -33,7 +33,7 @@ public:
|
||||
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 @@ public:
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -2725,7 +2725,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);
|
||||
|
||||
@ -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) ();
|
||||
|
||||
@ -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 *, ...);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user