-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[CIR] Defer emitting function definitions #142862
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
Conversation
This change implements deferring function definition emission until first use.
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis change implements deferring function definition emission until first use. Full diff: https://github.com/llvm/llvm-project/pull/142862.diff 2 Files Affected:
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()
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, minor nit
@@ -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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to diminish levels of indentation, should this return early otherwise?
if (dontDefer)
return funcOp
// rest of code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an assertion below this in the incubator and classic codegen (which I might as well add now), but it's easily moved to the early return.
It seems to me that dontDefer
is a somewhat misleading name. If it is true, it means that we aren't ready to move this from the deferredDecls
list to the declsToEmit
list yet. Nothing gets deferred here either way. It really should be something like dontMoveToEmitList
which is really closer to dontStopDeferring
. The current name dates back to OGCG. I think any renaming can wait for a future change. I just thought I'd mention it here.
✅ With the latest revision this PR passed the C/C++ code formatter. |
This change implements deferring function definition emission until first use.