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