Skip to content

Commit cd8b3ef

Browse files
andykaylorrorth
authored andcommitted
[CIR] Defer emitting function definitions (llvm#142862)
This change implements deferring function definition emission until first use.
1 parent f52030f commit cd8b3ef

File tree

2 files changed

+97
-9
lines changed

2 files changed

+97
-9
lines changed

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,18 @@ CIRGenModule::getAddrOfGlobal(GlobalDecl gd, ForDefinition_t isForDefinition) {
213213
}
214214

215215
if (isa<CXXMethodDecl>(d)) {
216-
errorNYI(d->getSourceRange(), "getAddrOfGlobal: C++ method decl");
217-
return nullptr;
216+
const CIRGenFunctionInfo &fi =
217+
getTypes().arrangeCXXMethodDeclaration(cast<CXXMethodDecl>(d));
218+
cir::FuncType ty = getTypes().getFunctionType(fi);
219+
return getAddrOfFunction(gd, ty, /*ForVTable=*/false, /*DontDefer=*/false,
220+
isForDefinition);
218221
}
219222

220223
if (isa<FunctionDecl>(d)) {
221-
errorNYI(d->getSourceRange(), "getAddrOfGlobal: function decl");
222-
return nullptr;
224+
const CIRGenFunctionInfo &fi = getTypes().arrangeGlobalDeclaration(gd);
225+
cir::FuncType ty = getTypes().getFunctionType(fi);
226+
return getAddrOfFunction(gd, ty, /*ForVTable=*/false, /*DontDefer=*/false,
227+
isForDefinition);
223228
}
224229

225230
return getAddrOfGlobalVar(cast<VarDecl>(d), /*ty=*/nullptr, isForDefinition)
@@ -1275,11 +1280,6 @@ bool CIRGenModule::mustBeEmitted(const ValueDecl *global) {
12751280
vd->getType().isConstQualified())))
12761281
return true;
12771282

1278-
// TODO(cir): We do want to defer function decls, but it's not implemented.
1279-
assert(!cir::MissingFeatures::deferredFuncDecls());
1280-
if (isa<FunctionDecl>(global))
1281-
return true;
1282-
12831283
return getASTContext().DeclMustBeEmitted(global);
12841284
}
12851285

@@ -1523,6 +1523,56 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction(
15231523
cir::FuncOp funcOp = createCIRFunction(
15241524
invalidLoc ? theModule->getLoc() : getLoc(funcDecl->getSourceRange()),
15251525
mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl);
1526+
1527+
// 'dontDefer' actually means don't move this to the deferredDeclsToEmit list.
1528+
if (dontDefer) {
1529+
// TODO(cir): This assertion will need an additional condition when we
1530+
// support incomplete functions.
1531+
assert(funcOp.getFunctionType() == funcType);
1532+
return funcOp;
1533+
}
1534+
1535+
// All MSVC dtors other than the base dtor are linkonce_odr and delegate to
1536+
// each other bottoming out wiht the base dtor. Therefore we emit non-base
1537+
// dtors on usage, even if there is no dtor definition in the TU.
1538+
if (isa_and_nonnull<CXXDestructorDecl>(d))
1539+
errorNYI(d->getSourceRange(), "getOrCreateCIRFunction: dtor");
1540+
1541+
// This is the first use or definition of a mangled name. If there is a
1542+
// deferred decl with this name, remember that we need to emit it at the end
1543+
// of the file.
1544+
auto ddi = deferredDecls.find(mangledName);
1545+
if (ddi != deferredDecls.end()) {
1546+
// Move the potentially referenced deferred decl to the
1547+
// DeferredDeclsToEmit list, and remove it from DeferredDecls (since we
1548+
// don't need it anymore).
1549+
addDeferredDeclToEmit(ddi->second);
1550+
deferredDecls.erase(ddi);
1551+
1552+
// Otherwise, there are cases we have to worry about where we're using a
1553+
// declaration for which we must emit a definition but where we might not
1554+
// find a top-level definition.
1555+
// - member functions defined inline in their classes
1556+
// - friend functions defined inline in some class
1557+
// - special member functions with implicit definitions
1558+
// If we ever change our AST traversal to walk into class methods, this
1559+
// will be unnecessary.
1560+
//
1561+
// We also don't emit a definition for a function if it's going to be an
1562+
// entry in a vtable, unless it's already marked as used.
1563+
} else if (getLangOpts().CPlusPlus && d) {
1564+
// Look for a declaration that's lexically in a record.
1565+
for (const auto *fd = cast<FunctionDecl>(d)->getMostRecentDecl(); fd;
1566+
fd = fd->getPreviousDecl()) {
1567+
if (isa<CXXRecordDecl>(fd->getLexicalDeclContext())) {
1568+
if (fd->doesThisDeclarationHaveABody()) {
1569+
addDeferredDeclToEmit(gd.getWithDecl(fd));
1570+
break;
1571+
}
1572+
}
1573+
}
1574+
}
1575+
15261576
return funcOp;
15271577
}
15281578

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR --implicit-check-not=externNotCalled \
3+
// RUN: --implicit-check-not=internalNotCalled --implicit-check-not=inlineNotCalled
4+
5+
extern int externCalled();
6+
extern int externNotCalled();
7+
8+
namespace {
9+
int internalCalled() { return 1; }
10+
int internalNotCalled() { return 2; }
11+
}
12+
13+
struct S {
14+
int inlineCalled() { return 3; }
15+
int inlineNotCalled() { return 4; }
16+
};
17+
18+
void use() {
19+
S s;
20+
externCalled();
21+
internalCalled();
22+
s.inlineCalled();
23+
}
24+
25+
// CIR: cir.func{{.*}} @_Z12externCalledv
26+
// This shouldn't have a body.
27+
// CIR-NOT: cir.return
28+
29+
// CIR: cir.func{{.*}} @_ZN12_GLOBAL__N_114internalCalledEv
30+
// CIR: %[[ONE:.*]] = cir.const #cir.int<1>
31+
// CIR: cir.store %[[ONE]], %[[RET_ADDR:.*]]
32+
33+
// CIR: cir.func{{.*}} @_ZN1S12inlineCalledEv
34+
// CIR: %[[THIS:.*]] = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["this", init]
35+
// CIR: %[[THREE:.*]] = cir.const #cir.int<3>
36+
// CIR: cir.store %[[THREE]], %[[RET_ADDR:.*]]
37+
38+
// CIR: cir.func{{.*}} @_Z3usev()

0 commit comments

Comments
 (0)