Skip to content

[clang][bytecode] Support ObjC blocks #104551

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ByteCode/ByteCodeEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand Down
15 changes: 11 additions & 4 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ByteCode/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ByteCode/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
14 changes: 9 additions & 5 deletions clang/lib/AST/ByteCode/Function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}
49 changes: 36 additions & 13 deletions clang/lib/AST/ByteCode/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -135,29 +144,38 @@ 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;
}

/// 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;
}

/// 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;
}
Expand All @@ -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; }

Expand All @@ -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;
}
Expand All @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
7 changes: 5 additions & 2 deletions clang/lib/AST/ByteCode/FunctionPointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions clang/test/Sema/block-misc.c
Original file line number Diff line number Diff line change
@@ -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) ();
Expand Down
1 change: 1 addition & 0 deletions clang/test/Sema/block-return.c
Original file line number Diff line number Diff line change
@@ -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 *, ...);

Expand Down
1 change: 1 addition & 0 deletions clang/test/SemaCXX/consteval-cleanup.cpp
Original file line number Diff line number Diff line change
@@ -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

Expand Down
Loading