[clang] 83356f3 - [clang][bytecode] Compile functions lazily (#131596)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 17 07:58:39 PDT 2025
Author: Timm Baeder
Date: 2025-03-17T15:58:35+01:00
New Revision: 83356f3b62e95b980ca48083aafa4a1b4040d4c9
URL: https://github.com/llvm/llvm-project/commit/83356f3b62e95b980ca48083aafa4a1b4040d4c9
DIFF: https://github.com/llvm/llvm-project/commit/83356f3b62e95b980ca48083aafa4a1b4040d4c9.diff
LOG: [clang][bytecode] Compile functions lazily (#131596)
Create the Function* handles for all functions we see, but delay the
actual compilation until we really call the function. This speeds up
compile times with the new interpreter a bit.
Added:
Modified:
clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
clang/lib/AST/ByteCode/ByteCodeEmitter.h
clang/lib/AST/ByteCode/Compiler.cpp
clang/lib/AST/ByteCode/Context.cpp
clang/lib/AST/ByteCode/Context.h
clang/lib/AST/ByteCode/Function.cpp
clang/lib/AST/ByteCode/Function.h
clang/lib/AST/ByteCode/Interp.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
index 4162b55070da9..d91d5f16fc7a9 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
@@ -20,149 +20,64 @@
using namespace clang;
using namespace clang::interp;
-Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
+void ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl,
+ Function *Func) {
+ assert(FuncDecl);
+ assert(Func);
// Manually created functions that haven't been assigned proper
// parameters yet.
if (!FuncDecl->param_empty() && !FuncDecl->param_begin())
- return nullptr;
+ return;
+
+ if (!FuncDecl->isDefined())
+ return;
- bool IsLambdaStaticInvoker = false;
+ // Set up lambda captures.
if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl);
- MD && MD->isLambdaStaticInvoker()) {
- // For a lambda static invoker, we might have to pick a specialized
- // version if the lambda is generic. In that case, the picked function
- // will *NOT* be a static invoker anymore. However, it will still
- // be a non-static member function, this (usually) requiring an
- // instance pointer. We suppress that later in this function.
- IsLambdaStaticInvoker = true;
-
- const CXXRecordDecl *ClosureClass = MD->getParent();
- assert(ClosureClass->captures_begin() == ClosureClass->captures_end());
- if (ClosureClass->isGenericLambda()) {
- const CXXMethodDecl *LambdaCallOp = ClosureClass->getLambdaCallOperator();
- assert(MD->isFunctionTemplateSpecialization() &&
- "A generic lambda's static-invoker function must be a "
- "template specialization");
- const TemplateArgumentList *TAL = MD->getTemplateSpecializationArgs();
- FunctionTemplateDecl *CallOpTemplate =
- LambdaCallOp->getDescribedFunctionTemplate();
- void *InsertPos = nullptr;
- const FunctionDecl *CorrespondingCallOpSpecialization =
- CallOpTemplate->findSpecialization(TAL->asArray(), InsertPos);
- assert(CorrespondingCallOpSpecialization);
- FuncDecl = cast<CXXMethodDecl>(CorrespondingCallOpSpecialization);
- }
- }
+ MD && isLambdaCallOperator(MD)) {
+ // Set up lambda capture to closure record field mapping.
+ const Record *R = P.getOrCreateRecord(MD->getParent());
+ assert(R);
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> LC;
+ FieldDecl *LTC;
- // Set up argument indices.
- unsigned ParamOffset = 0;
- SmallVector<PrimType, 8> ParamTypes;
- SmallVector<unsigned, 8> ParamOffsets;
- llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors;
-
- // If the return is not a primitive, a pointer to the storage where the
- // value is initialized in is passed as the first argument. See 'RVO'
- // elsewhere in the code.
- QualType Ty = FuncDecl->getReturnType();
- bool HasRVO = false;
- if (!Ty->isVoidType() && !Ctx.classify(Ty)) {
- HasRVO = true;
- ParamTypes.push_back(PT_Ptr);
- ParamOffsets.push_back(ParamOffset);
- ParamOffset += align(primSize(PT_Ptr));
- }
+ MD->getParent()->getCaptureFields(LC, LTC);
- // If the function decl is a member decl, the next parameter is
- // the 'this' pointer. This parameter is pop()ed from the
- // InterpStack when calling the function.
- bool HasThisPointer = false;
- if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl)) {
- if (!IsLambdaStaticInvoker) {
- HasThisPointer = MD->isInstance();
- if (MD->isImplicitObjectMemberFunction()) {
- ParamTypes.push_back(PT_Ptr);
- ParamOffsets.push_back(ParamOffset);
- ParamOffset += align(primSize(PT_Ptr));
- }
+ for (auto Cap : LC) {
+ unsigned Offset = R->getField(Cap.second)->Offset;
+ this->LambdaCaptures[Cap.first] = {
+ Offset, Cap.second->getType()->isReferenceType()};
}
-
- // Set up lambda capture to closure record field mapping.
- if (isLambdaCallOperator(MD)) {
- // The parent record needs to be complete, we need to know about all
- // the lambda captures.
- if (!MD->getParent()->isCompleteDefinition())
- return nullptr;
-
- const Record *R = P.getOrCreateRecord(MD->getParent());
- llvm::DenseMap<const ValueDecl *, FieldDecl *> LC;
- FieldDecl *LTC;
-
- MD->getParent()->getCaptureFields(LC, LTC);
-
- for (auto Cap : LC) {
- // Static lambdas cannot have any captures. If this one does,
- // it has already been diagnosed and we can only ignore it.
- if (MD->isStatic())
- return nullptr;
-
- unsigned Offset = R->getField(Cap.second)->Offset;
- this->LambdaCaptures[Cap.first] = {
- Offset, Cap.second->getType()->isReferenceType()};
- }
- if (LTC) {
- QualType CaptureType = R->getField(LTC)->Decl->getType();
- this->LambdaThisCapture = {R->getField(LTC)->Offset,
- CaptureType->isReferenceType() ||
- CaptureType->isPointerType()};
- }
+ if (LTC) {
+ QualType CaptureType = R->getField(LTC)->Decl->getType();
+ this->LambdaThisCapture = {R->getField(LTC)->Offset,
+ CaptureType->isPointerOrReferenceType()};
}
}
- // Assign descriptors to all parameters.
- // Composite objects are lowered to pointers.
- for (const ParmVarDecl *PD : FuncDecl->parameters()) {
+ // Register parameters with their offset.
+ unsigned ParamIndex = 0;
+ unsigned Drop = Func->hasRVO() +
+ (Func->hasThisPointer() && !Func->isThisPointerExplicit());
+ for (auto ParamOffset : llvm::drop_begin(Func->ParamOffsets, Drop)) {
+ const ParmVarDecl *PD = FuncDecl->parameters()[ParamIndex];
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);
- }
-
- // Create a handle over the emitted code.
- Function *Func = P.getFunction(FuncDecl);
- if (!Func) {
- Func = P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes),
- std::move(ParamDescriptors),
- std::move(ParamOffsets), HasThisPointer, HasRVO);
- }
-
- assert(Func);
- // For not-yet-defined functions, we only create a Function instance and
- // compile their body later.
- if (!FuncDecl->isDefined() ||
- (FuncDecl->willHaveBody() && !FuncDecl->hasBody())) {
- Func->setDefined(false);
- return Func;
+ this->Params.insert({PD, {ParamOffset, T != std::nullopt}});
+ ++ParamIndex;
}
Func->setDefined(true);
// Lambda static invokers are a special case that we emit custom code for.
- bool IsEligibleForCompilation = false;
- if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl))
- IsEligibleForCompilation = MD->isLambdaStaticInvoker();
- if (!IsEligibleForCompilation)
- IsEligibleForCompilation =
- FuncDecl->isConstexpr() || FuncDecl->hasAttr<MSConstexprAttr>();
+ bool IsEligibleForCompilation = Func->isLambdaStaticInvoker() ||
+ FuncDecl->isConstexpr() ||
+ FuncDecl->hasAttr<MSConstexprAttr>();
// Compile the function body.
if (!IsEligibleForCompilation || !visitFunc(FuncDecl)) {
Func->setIsFullyCompiled(true);
- return Func;
+ return;
}
// Create scopes from descriptors.
@@ -175,48 +90,6 @@ Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap),
std::move(Scopes), FuncDecl->hasBody());
Func->setIsFullyCompiled(true);
- 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);
-
- 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) {
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.h b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
index 64670c32cbcf6..5c7482923386e 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
@@ -31,8 +31,7 @@ class ByteCodeEmitter {
public:
/// Compiles the function into the module.
- Function *compileFunc(const FunctionDecl *FuncDecl);
- Function *compileObjCBlock(const BlockExpr *BE);
+ void compileFunc(const FunctionDecl *FuncDecl, Function *Func = nullptr);
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 b9f88230007b5..3524ab5f86de8 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3576,7 +3576,7 @@ bool Compiler<Emitter>::VisitBlockExpr(const BlockExpr *E) {
return true;
const Function *Func = nullptr;
- if (auto F = Compiler<ByteCodeEmitter>(Ctx, P).compileObjCBlock(E))
+ if (auto F = Ctx.getOrCreateObjCBlock(E))
Func = F;
if (!Func)
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index aa434d5c85921..23f4c5a4fa4b7 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -27,10 +27,17 @@ Context::~Context() {}
bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
assert(Stk.empty());
+
+ // Get a function handle.
const Function *Func = getOrCreateFunction(FD);
if (!Func)
return false;
+ // Compile the function.
+ Compiler<ByteCodeEmitter>(*this, *P).compileFunc(
+ FD, const_cast<Function *>(Func));
+
+ // And run it.
if (!Run(Parent, Func))
return false;
@@ -263,21 +270,149 @@ Context::getOverridingFunction(const CXXRecordDecl *DynamicDecl,
return nullptr;
}
-const Function *Context::getOrCreateFunction(const FunctionDecl *FD) {
- assert(FD);
- FD = FD->getMostRecentDecl();
- const Function *Func = P->getFunction(FD);
- bool IsBeingCompiled = Func && Func->isDefined() && !Func->isFullyCompiled();
- bool WasNotDefined = Func && !Func->isConstexpr() && !Func->isDefined();
+const Function *Context::getOrCreateFunction(const FunctionDecl *FuncDecl) {
+ assert(FuncDecl);
+ FuncDecl = FuncDecl->getMostRecentDecl();
- if (IsBeingCompiled)
+ if (const Function *Func = P->getFunction(FuncDecl))
return Func;
- if (!Func || WasNotDefined) {
- if (auto F = Compiler<ByteCodeEmitter>(*this, *P).compileFunc(FD))
- Func = F;
+ // Manually created functions that haven't been assigned proper
+ // parameters yet.
+ if (!FuncDecl->param_empty() && !FuncDecl->param_begin())
+ return nullptr;
+
+ bool IsLambdaStaticInvoker = false;
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl);
+ MD && MD->isLambdaStaticInvoker()) {
+ // For a lambda static invoker, we might have to pick a specialized
+ // version if the lambda is generic. In that case, the picked function
+ // will *NOT* be a static invoker anymore. However, it will still
+ // be a non-static member function, this (usually) requiring an
+ // instance pointer. We suppress that later in this function.
+ IsLambdaStaticInvoker = true;
+
+ const CXXRecordDecl *ClosureClass = MD->getParent();
+ assert(ClosureClass->captures_begin() == ClosureClass->captures_end());
+ if (ClosureClass->isGenericLambda()) {
+ const CXXMethodDecl *LambdaCallOp = ClosureClass->getLambdaCallOperator();
+ assert(MD->isFunctionTemplateSpecialization() &&
+ "A generic lambda's static-invoker function must be a "
+ "template specialization");
+ const TemplateArgumentList *TAL = MD->getTemplateSpecializationArgs();
+ FunctionTemplateDecl *CallOpTemplate =
+ LambdaCallOp->getDescribedFunctionTemplate();
+ void *InsertPos = nullptr;
+ const FunctionDecl *CorrespondingCallOpSpecialization =
+ CallOpTemplate->findSpecialization(TAL->asArray(), InsertPos);
+ assert(CorrespondingCallOpSpecialization);
+ FuncDecl = CorrespondingCallOpSpecialization;
+ }
+ }
+ // Set up argument indices.
+ unsigned ParamOffset = 0;
+ SmallVector<PrimType, 8> ParamTypes;
+ SmallVector<unsigned, 8> ParamOffsets;
+ llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors;
+
+ // If the return is not a primitive, a pointer to the storage where the
+ // value is initialized in is passed as the first argument. See 'RVO'
+ // elsewhere in the code.
+ QualType Ty = FuncDecl->getReturnType();
+ bool HasRVO = false;
+ if (!Ty->isVoidType() && !classify(Ty)) {
+ HasRVO = true;
+ ParamTypes.push_back(PT_Ptr);
+ ParamOffsets.push_back(ParamOffset);
+ ParamOffset += align(primSize(PT_Ptr));
+ }
+
+ // If the function decl is a member decl, the next parameter is
+ // the 'this' pointer. This parameter is pop()ed from the
+ // InterpStack when calling the function.
+ bool HasThisPointer = false;
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl)) {
+ if (!IsLambdaStaticInvoker) {
+ HasThisPointer = MD->isInstance();
+ if (MD->isImplicitObjectMemberFunction()) {
+ ParamTypes.push_back(PT_Ptr);
+ ParamOffsets.push_back(ParamOffset);
+ ParamOffset += align(primSize(PT_Ptr));
+ }
+ }
+
+ if (isLambdaCallOperator(MD)) {
+ // The parent record needs to be complete, we need to know about all
+ // the lambda captures.
+ if (!MD->getParent()->isCompleteDefinition())
+ return nullptr;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> LC;
+ FieldDecl *LTC;
+
+ MD->getParent()->getCaptureFields(LC, LTC);
+
+ if (MD->isStatic() && !LC.empty()) {
+ // Static lambdas cannot have any captures. If this one does,
+ // it has already been diagnosed and we can only ignore it.
+ return nullptr;
+ }
+ }
+ }
+
+ // Assign descriptors to all parameters.
+ // Composite objects are lowered to pointers.
+ for (const ParmVarDecl *PD : FuncDecl->parameters()) {
+ std::optional<PrimType> T = classify(PD->getType());
+ PrimType PT = T.value_or(PT_Ptr);
+ Descriptor *Desc = P->createDescriptor(PD, PT);
+ ParamDescriptors.insert({ParamOffset, {PT, Desc}});
+ ParamOffsets.push_back(ParamOffset);
+ ParamOffset += align(primSize(PT));
+ ParamTypes.push_back(PT);
}
+ // Create a handle over the emitted code.
+ assert(!P->getFunction(FuncDecl));
+ const Function *Func = P->createFunction(
+ FuncDecl, ParamOffset, std::move(ParamTypes), std::move(ParamDescriptors),
+ std::move(ParamOffsets), HasThisPointer, HasRVO, IsLambdaStaticInvoker);
+ return Func;
+}
+
+const Function *Context::getOrCreateObjCBlock(const BlockExpr *E) {
+ const BlockDecl *BD = E->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 = classify(PD->getType());
+ PrimType PT = T.value_or(PT_Ptr);
+ Descriptor *Desc = P->createDescriptor(PD, PT);
+ ParamDescriptors.insert({ParamOffset, {PT, Desc}});
+ 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(E, ParamOffset, std::move(ParamTypes),
+ std::move(ParamDescriptors), std::move(ParamOffsets),
+ /*HasThisPointer=*/false, /*HasRVO=*/false,
+ /*IsLambdaStaticInvoker=*/false);
+
+ assert(Func);
+ Func->setDefined(true);
+ // We don't compile the BlockDecl code at all right now.
+ Func->setIsFullyCompiled(true);
return Func;
}
diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h
index ed539def99efd..8e142a0121ed3 100644
--- a/clang/lib/AST/ByteCode/Context.h
+++ b/clang/lib/AST/ByteCode/Context.h
@@ -24,6 +24,7 @@ class LangOptions;
class FunctionDecl;
class VarDecl;
class APValue;
+class BlockExpr;
namespace interp {
class Function;
@@ -91,7 +92,8 @@ class Context final {
const CXXRecordDecl *StaticDecl,
const CXXMethodDecl *InitialFunction) const;
- const Function *getOrCreateFunction(const FunctionDecl *FD);
+ const Function *getOrCreateFunction(const FunctionDecl *FuncDecl);
+ const Function *getOrCreateObjCBlock(const BlockExpr *E);
/// Returns whether we should create a global variable for the
/// given ValueDecl.
diff --git a/clang/lib/AST/ByteCode/Function.cpp b/clang/lib/AST/ByteCode/Function.cpp
index 6b892dfd616c1..fa803070c821d 100644
--- a/clang/lib/AST/ByteCode/Function.cpp
+++ b/clang/lib/AST/ByteCode/Function.cpp
@@ -19,7 +19,7 @@ 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 HasThisPointer, bool HasRVO, bool IsLambdaStaticInvoker)
: P(P), Kind(FunctionKind::Normal), Source(Source), ArgSize(ArgSize),
ParamTypes(std::move(ParamTypes)), Params(std::move(Params)),
ParamOffsets(std::move(ParamOffsets)), HasThisPointer(HasThisPointer),
@@ -35,7 +35,7 @@ Function::Function(Program &P, FunctionDeclTy Source, unsigned ArgSize,
Kind = FunctionKind::Dtor;
} else if (const auto *MD = dyn_cast<CXXMethodDecl>(F)) {
Virtual = MD->isVirtual();
- if (MD->isLambdaStaticInvoker())
+ if (IsLambdaStaticInvoker) // MD->isLambdaStaticInvoker())
Kind = FunctionKind::LambdaStaticInvoker;
else if (clang::isLambdaCallOperator(F))
Kind = FunctionKind::LambdaCallOperator;
diff --git a/clang/lib/AST/ByteCode/Function.h b/clang/lib/AST/ByteCode/Function.h
index e17183eef9eac..cdf98f9e67dde 100644
--- a/clang/lib/AST/ByteCode/Function.h
+++ b/clang/lib/AST/ByteCode/Function.h
@@ -232,7 +232,7 @@ class Function final {
llvm::SmallVectorImpl<PrimType> &&ParamTypes,
llvm::DenseMap<unsigned, ParamDescriptor> &&Params,
llvm::SmallVectorImpl<unsigned> &&ParamOffsets, bool HasThisPointer,
- bool HasRVO);
+ bool HasRVO, bool IsLambdaStaticInvoker);
/// Sets the code of a function.
void setCode(unsigned NewFrameSize, std::vector<std::byte> &&NewCode,
@@ -252,6 +252,7 @@ class Function final {
private:
friend class Program;
friend class ByteCodeEmitter;
+ friend class Context;
/// Program reference.
Program &P;
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 201fec3e864d5..ffd2b31147d20 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "Interp.h"
+#include "Compiler.h"
#include "Function.h"
#include "InterpFrame.h"
#include "InterpShared.h"
@@ -497,6 +498,8 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
if (!Ptr.isStatic() || !Ptr.isBlockPointer())
return true;
+ if (!Ptr.getDeclID())
+ return true;
return CheckConstant(S, OpPC, Ptr.getDeclDesc());
}
@@ -1355,6 +1358,11 @@ static bool checkConstructor(InterpState &S, CodePtr OpPC, const Function *Func,
return false;
}
+static void compileFunction(InterpState &S, const Function *Func) {
+ Compiler<ByteCodeEmitter>(S.getContext(), S.P)
+ .compileFunc(Func->getDecl(), const_cast<Function *>(Func));
+}
+
bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
uint32_t VarArgSize) {
if (Func->hasThisPointer()) {
@@ -1377,6 +1385,9 @@ bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
return false;
}
+ if (!Func->isFullyCompiled())
+ compileFunction(S, Func);
+
if (!CheckCallable(S, OpPC, Func))
return false;
@@ -1401,7 +1412,6 @@ bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
S.Current = FrameBefore;
return false;
}
-
bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
uint32_t VarArgSize) {
assert(Func);
@@ -1442,6 +1452,9 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
return false;
}
+ if (!Func->isFullyCompiled())
+ compileFunction(S, Func);
+
if (!CheckCallable(S, OpPC, Func))
return cleanup();
@@ -1484,6 +1497,9 @@ bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);
const FunctionDecl *Callee = Func->getDecl();
+ if (!Func->isFullyCompiled())
+ compileFunction(S, Func);
+
// C++2a [class.abstract]p6:
// the effect of making a virtual call to a pure virtual function [...] is
// undefined
More information about the cfe-commits
mailing list