https://github.com/andykaylor updated 
https://github.com/llvm/llvm-project/pull/142862

>From 217f3a01f9ed161eb3afa3f7e6a594720e8a2840 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akay...@nvidia.com>
Date: Mon, 2 Jun 2025 17:11:55 -0700
Subject: [PATCH 1/3] [CIR] Defer emitting function definitions

This change implements deferring function definition emission until first
use.
---
 clang/lib/CIR/CodeGen/CIRGenModule.cpp      | 62 ++++++++++++++++++---
 clang/test/CIR/CodeGen/deferred-fn-defs.cpp | 38 +++++++++++++
 2 files changed, 91 insertions(+), 9 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/deferred-fn-defs.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index eb291de8a3cc9..54c9fcf629e3d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -213,13 +213,18 @@ CIRGenModule::getAddrOfGlobal(GlobalDecl gd, 
ForDefinition_t isForDefinition) {
   }
 
   if (isa<CXXMethodDecl>(d)) {
-    errorNYI(d->getSourceRange(), "getAddrOfGlobal: C++ method decl");
-    return nullptr;
+    const CIRGenFunctionInfo &fi =
+        getTypes().arrangeCXXMethodDeclaration(cast<CXXMethodDecl>(d));
+    cir::FuncType ty = getTypes().getFunctionType(fi);
+    return getAddrOfFunction(gd, ty, /*ForVTable=*/false, /*DontDefer=*/false,
+                             isForDefinition);
   }
 
   if (isa<FunctionDecl>(d)) {
-    errorNYI(d->getSourceRange(), "getAddrOfGlobal: function decl");
-    return nullptr;
+    const CIRGenFunctionInfo &fi = getTypes().arrangeGlobalDeclaration(gd);
+    cir::FuncType ty = getTypes().getFunctionType(fi);
+    return getAddrOfFunction(gd, ty, /*ForVTable=*/false, /*DontDefer=*/false,
+                             isForDefinition);
   }
 
   return getAddrOfGlobalVar(cast<VarDecl>(d), /*ty=*/nullptr, isForDefinition)
@@ -1275,11 +1280,6 @@ bool CIRGenModule::mustBeEmitted(const ValueDecl 
*global) {
         vd->getType().isConstQualified())))
     return true;
 
-  // TODO(cir): We do want to defer function decls, but it's not implemented.
-  assert(!cir::MissingFeatures::deferredFuncDecls());
-  if (isa<FunctionDecl>(global))
-    return true;
-
   return getASTContext().DeclMustBeEmitted(global);
 }
 
@@ -1523,6 +1523,50 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
   cir::FuncOp funcOp = createCIRFunction(
       invalidLoc ? theModule->getLoc() : getLoc(funcDecl->getSourceRange()),
       mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl);
+
+  if (!dontDefer) {
+    // All MSVC dtors other than the base dtor are linkonce_odr and delegate to
+    // each other bottoming out wiht the base dtor. Therefore we emit non-base
+    // dtors on usage, even if there is no dtor definition in the TU.
+    if (isa_and_nonnull<CXXDestructorDecl>(d))
+      errorNYI(d->getSourceRange(), "getOrCreateCIRFunction: dtor");
+
+    // This is the first use or definition of a mangled name. If there is a
+    // deferred decl with this name, remember that we need to emit it at the 
end
+    // of the file.
+    auto ddi = deferredDecls.find(mangledName);
+    if (ddi != deferredDecls.end()) {
+      // Move the potentially referenced deferred decl to the
+      // DeferredDeclsToEmit list, and remove it from DeferredDecls (since we
+      // don't need it anymore).
+      addDeferredDeclToEmit(ddi->second);
+      deferredDecls.erase(ddi);
+
+      // Otherwise, there are cases we have to worry about where we're using a
+      // declaration for which we must emit a definition but where we might not
+      // find a top-level definition.
+      //   - member functions defined inline in their classes
+      //   - friend functions defined inline in some class
+      //   - special member functions with implicit definitions
+      // If we ever change our AST traversal to walk into class methods, this
+      // will be unnecessary.
+      //
+      // We also don't emit a definition for a function if it's going to be an
+      // entry in a vtable, unless it's already marked as used.
+    } else if (getLangOpts().CPlusPlus && d) {
+      // Look for a declaration that's lexically in a record.
+      for (const auto *fd = cast<FunctionDecl>(d)->getMostRecentDecl(); fd;
+           fd = fd->getPreviousDecl()) {
+        if (isa<CXXRecordDecl>(fd->getLexicalDeclContext())) {
+          if (fd->doesThisDeclarationHaveABody()) {
+            addDeferredDeclToEmit(gd.getWithDecl(fd));
+            break;
+          }
+        }
+      }
+    }
+  }
+
   return funcOp;
 }
 
diff --git a/clang/test/CIR/CodeGen/deferred-fn-defs.cpp 
b/clang/test/CIR/CodeGen/deferred-fn-defs.cpp
new file mode 100644
index 0000000000000..e7088ba35eca7
--- /dev/null
+++ b/clang/test/CIR/CodeGen/deferred-fn-defs.cpp
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR 
--implicit-check-not=externNotCalled \
+// RUN:   --implicit-check-not=internalNotCalled 
--implicit-check-not=inlineNotCalled
+
+extern int externCalled();
+extern int externNotCalled();
+
+namespace {
+  int internalCalled() { return 1; }
+  int internalNotCalled() { return 2; }
+}
+
+struct S {
+  int inlineCalled() { return 3; }
+  int inlineNotCalled() { return 4; }
+};
+
+void use() {
+  S s;
+  externCalled();
+  internalCalled();
+  s.inlineCalled();
+}
+
+// CIR: cir.func{{.*}} @_Z12externCalledv
+// This shouldn't have a body.
+// CIR-NOT: cir.return
+
+// CIR: cir.func{{.*}} @_ZN12_GLOBAL__N_114internalCalledEv
+// CIR:   %[[ONE:.*]] = cir.const #cir.int<1>
+// CIR:   cir.store %[[ONE]], %[[RET_ADDR:.*]]
+
+// CIR: cir.func{{.*}} @_ZN1S12inlineCalledEv
+// CIR:   %[[THIS:.*]] = cir.alloca !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>, ["this", init]
+// CIR:   %[[THREE:.*]] = cir.const #cir.int<3>
+// CIR:   cir.store %[[THREE]], %[[RET_ADDR:.*]]
+
+// CIR: cir.func{{.*}} @_Z3usev()

>From 27bb7d528630bf298d3ea3315dbfd5e06949c71c Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akay...@nvidia.com>
Date: Wed, 4 Jun 2025 16:56:44 -0700
Subject: [PATCH 2/3] Implement review suggestion

---
 clang/lib/CIR/CodeGen/CIRGenModule.cpp | 82 ++++++++++++++------------
 1 file changed, 44 insertions(+), 38 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 54c9fcf629e3d..0ad674344d1f1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1524,44 +1524,50 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
       invalidLoc ? theModule->getLoc() : getLoc(funcDecl->getSourceRange()),
       mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl);
 
-  if (!dontDefer) {
-    // All MSVC dtors other than the base dtor are linkonce_odr and delegate to
-    // each other bottoming out wiht the base dtor. Therefore we emit non-base
-    // dtors on usage, even if there is no dtor definition in the TU.
-    if (isa_and_nonnull<CXXDestructorDecl>(d))
-      errorNYI(d->getSourceRange(), "getOrCreateCIRFunction: dtor");
-
-    // This is the first use or definition of a mangled name. If there is a
-    // deferred decl with this name, remember that we need to emit it at the 
end
-    // of the file.
-    auto ddi = deferredDecls.find(mangledName);
-    if (ddi != deferredDecls.end()) {
-      // Move the potentially referenced deferred decl to the
-      // DeferredDeclsToEmit list, and remove it from DeferredDecls (since we
-      // don't need it anymore).
-      addDeferredDeclToEmit(ddi->second);
-      deferredDecls.erase(ddi);
-
-      // Otherwise, there are cases we have to worry about where we're using a
-      // declaration for which we must emit a definition but where we might not
-      // find a top-level definition.
-      //   - member functions defined inline in their classes
-      //   - friend functions defined inline in some class
-      //   - special member functions with implicit definitions
-      // If we ever change our AST traversal to walk into class methods, this
-      // will be unnecessary.
-      //
-      // We also don't emit a definition for a function if it's going to be an
-      // entry in a vtable, unless it's already marked as used.
-    } else if (getLangOpts().CPlusPlus && d) {
-      // Look for a declaration that's lexically in a record.
-      for (const auto *fd = cast<FunctionDecl>(d)->getMostRecentDecl(); fd;
-           fd = fd->getPreviousDecl()) {
-        if (isa<CXXRecordDecl>(fd->getLexicalDeclContext())) {
-          if (fd->doesThisDeclarationHaveABody()) {
-            addDeferredDeclToEmit(gd.getWithDecl(fd));
-            break;
-          }
+  // 'dontDefer' actually means don't move this to the deferredDeclsToEmit 
list.
+  if (dontDefer) {
+    // TODO(cir): This assertion will need an additional condition when we
+    // support incomplete functions.
+    assert(funcOp.getFunctionType() == funcType);
+    return funcOp;
+  }
+
+  // All MSVC dtors other than the base dtor are linkonce_odr and delegate to
+  // each other bottoming out wiht the base dtor. Therefore we emit non-base
+  // dtors on usage, even if there is no dtor definition in the TU.
+  if (isa_and_nonnull<CXXDestructorDecl>(d))
+    errorNYI(d->getSourceRange(), "getOrCreateCIRFunction: dtor");
+
+  // This is the first use or definition of a mangled name. If there is a
+  // deferred decl with this name, remember that we need to emit it at the end
+  // of the file.
+  auto ddi = deferredDecls.find(mangledName);
+  if (ddi != deferredDecls.end()) {
+    // Move the potentially referenced deferred decl to the
+    // DeferredDeclsToEmit list, and remove it from DeferredDecls (since we
+    // don't need it anymore).
+    addDeferredDeclToEmit(ddi->second);
+    deferredDecls.erase(ddi);
+
+    // Otherwise, there are cases we have to worry about where we're using a
+    // declaration for which we must emit a definition but where we might not
+    // find a top-level definition.
+    //   - member functions defined inline in their classes
+    //   - friend functions defined inline in some class
+    //   - special member functions with implicit definitions
+    // If we ever change our AST traversal to walk into class methods, this
+    // will be unnecessary.
+    //
+    // We also don't emit a definition for a function if it's going to be an
+    // entry in a vtable, unless it's already marked as used.
+  } else if (getLangOpts().CPlusPlus && d) {
+    // Look for a declaration that's lexically in a record.
+    for (const auto *fd = cast<FunctionDecl>(d)->getMostRecentDecl(); fd;
+          fd = fd->getPreviousDecl()) {
+      if (isa<CXXRecordDecl>(fd->getLexicalDeclContext())) {
+        if (fd->doesThisDeclarationHaveABody()) {
+          addDeferredDeclToEmit(gd.getWithDecl(fd));
+          break;
         }
       }
     }

>From cc638daba965701f155fd01d9f5c3a52d5a1c2d4 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akay...@nvidia.com>
Date: Thu, 5 Jun 2025 10:09:05 -0700
Subject: [PATCH 3/3] Fix formatting

---
 clang/lib/CIR/CodeGen/CIRGenModule.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 0ad674344d1f1..87e364197ce7e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1563,7 +1563,7 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
   } else if (getLangOpts().CPlusPlus && d) {
     // Look for a declaration that's lexically in a record.
     for (const auto *fd = cast<FunctionDecl>(d)->getMostRecentDecl(); fd;
-          fd = fd->getPreviousDecl()) {
+         fd = fd->getPreviousDecl()) {
       if (isa<CXXRecordDecl>(fd->getLexicalDeclContext())) {
         if (fd->doesThisDeclarationHaveABody()) {
           addDeferredDeclToEmit(gd.getWithDecl(fd));

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to