[clang] af54790 - [CIR] Defer emitting function definitions (#142862)

via cfe-commits cfe-commits at lists.llvm.org
Thu Jun 5 13:08:32 PDT 2025


Author: Andy Kaylor
Date: 2025-06-05T13:08:29-07:00
New Revision: af54790ca0a3d55d58c2cd7c07d3c4e16c689c72

URL: https://github.com/llvm/llvm-project/commit/af54790ca0a3d55d58c2cd7c07d3c4e16c689c72
DIFF: https://github.com/llvm/llvm-project/commit/af54790ca0a3d55d58c2cd7c07d3c4e16c689c72.diff

LOG: [CIR] Defer emitting function definitions (#142862)

This change implements deferring function definition emission until
first use.

Added: 
    clang/test/CIR/CodeGen/deferred-fn-defs.cpp

Modified: 
    clang/lib/CIR/CodeGen/CIRGenModule.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index eb291de8a3cc9..87e364197ce7e 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,56 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
   cir::FuncOp funcOp = createCIRFunction(
       invalidLoc ? theModule->getLoc() : getLoc(funcDecl->getSourceRange()),
       mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl);
+
+  // '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;
+        }
+      }
+    }
+  }
+
   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()


        


More information about the cfe-commits mailing list