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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits