Skip to content

[clang][bytecode] Compile functions lazily #131596

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
Mar 17, 2025
Merged

Conversation

tbaederr
Copy link
Contributor

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.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Mar 17, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 17, 2025

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

Changes

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.


Patch is 22.45 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/131596.diff

8 Files Affected:

  • (modified) clang/lib/AST/ByteCode/ByteCodeEmitter.cpp (+35-163)
  • (modified) clang/lib/AST/ByteCode/ByteCodeEmitter.h (+1-2)
  • (modified) clang/lib/AST/ByteCode/Compiler.cpp (+1-1)
  • (modified) clang/lib/AST/ByteCode/Context.cpp (+145-10)
  • (modified) clang/lib/AST/ByteCode/Context.h (+3-1)
  • (modified) clang/lib/AST/ByteCode/Function.cpp (+2-2)
  • (modified) clang/lib/AST/ByteCode/Function.h (+2-1)
  • (modified) clang/lib/AST/ByteCode/Interp.cpp (+17-1)
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
index 4162b55070da9..68651861a271e 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp
@@ -20,149 +20,63 @@
 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->hasThisPointer() + Func->hasRVO();
+  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 +89,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,...
[truncated]

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.
@tbaederr tbaederr merged commit 83356f3 into llvm:main Mar 17, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants