Skip to content

Commit d98f8d3

Browse files
committed
Also emit inline functions that are used in variable initializers.
1 parent e9ca0c7 commit d98f8d3

File tree

3 files changed

+44
-10
lines changed

3 files changed

+44
-10
lines changed

lib/IRGen/GenClangDecl.cpp

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,37 @@ class ClangDeclRefFinder
3434
return true;
3535
}
3636
};
37+
38+
// If any (re)declaration of `decl` contains executable code, returns that
39+
// redeclaration; otherwise, returns nullptr.
40+
// In the case of a function, executable code is contained in the function
41+
// definition. In the case of a variable, executable code can be contained in
42+
// the initializer of the variable.
43+
clang::Decl *getDeclWithExecutableCode(clang::Decl *decl) {
44+
if (auto fd = dyn_cast<clang::FunctionDecl>(decl)) {
45+
const clang::FunctionDecl *definition;
46+
if (fd->hasBody(definition)) {
47+
return const_cast<clang::FunctionDecl *>(definition);
48+
}
49+
} else if (auto vd = dyn_cast<clang::VarDecl>(decl)) {
50+
clang::VarDecl *initializingDecl = vd->getInitializingDeclaration();
51+
if (initializingDecl) {
52+
return initializingDecl;
53+
}
54+
}
55+
56+
return nullptr;
57+
}
58+
3759
} // end anonymous namespace
3860

3961
void IRGenModule::emitClangDecl(const clang::Decl *decl) {
40-
auto valueDecl = dyn_cast<clang::ValueDecl>(decl);
41-
if (!valueDecl || valueDecl->isExternallyVisible()) {
62+
// Fast path for the case where `decl` doesn't contain executable code, so it
63+
// can't reference any other declarations that we would need to emit.
64+
if (getDeclWithExecutableCode(const_cast<clang::Decl *>(decl)) == nullptr) {
4265
ClangCodeGen->HandleTopLevelDecl(
4366
clang::DeclGroupRef(const_cast<clang::Decl*>(decl)));
44-
if (!valueDecl)
45-
return;
67+
return;
4668
}
4769

4870
if (!GlobalClangDecls.insert(decl->getCanonicalDecl()).second)
@@ -70,12 +92,9 @@ void IRGenModule::emitClangDecl(const clang::Decl *decl) {
7092

7193
while (!stack.empty()) {
7294
auto *next = const_cast<clang::Decl *>(stack.pop_back_val());
73-
if (auto fn = dyn_cast<clang::FunctionDecl>(next)) {
74-
const clang::FunctionDecl *definition;
75-
if (fn->hasBody(definition)) {
76-
refFinder.TraverseDecl(const_cast<clang::FunctionDecl *>(definition));
77-
next = const_cast<clang::FunctionDecl *>(definition);
78-
}
95+
if (clang::Decl *executableDecl = getDeclWithExecutableCode(next)) {
96+
refFinder.TraverseDecl(executableDecl);
97+
next = executableDecl;
7998
}
8099
ClangCodeGen->HandleTopLevelDecl(clang::DeclGroupRef(next));
81100
}

test/Interop/C/function/Inputs/emit-called-inline-function.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ class C {
2525
return 42;
2626
}
2727
};
28+
29+
inline int calledTransitivelyFromVarInit() {
30+
return 42;
31+
}
32+
33+
inline int varUsedFromSwift = calledTransitivelyFromVarInit();
34+
#else
35+
// C only allows constant initializers for variables with static storage
36+
// duration, so there's no way to initialize this with the result of a call to
37+
// an inline method. Just provide _some_ definition of `varImportedToSwift` so
38+
// we can import it in the test.
39+
static int varUsedFromSwift = 42;
2840
#endif
2941

3042
INLINE int calledFromSwift() {

test/Interop/C/function/emit-called-inline-function-irgen.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,8 @@ import EmitCalledInlineFunction
1616
// CXX-DAG: define linkonce_odr i32 @_Z15calledFromSwiftv() #{{[0-9]+}} comdat {
1717
// CXX-DAG: define linkonce_odr i32 @_Z18calledTransitivelyv() #{{[0-9]+}} comdat {
1818
// CXX-DAG: define linkonce_odr i32 @_ZN1C32memberFunctionCalledTransitivelyEv(%class.C* %this)
19+
// CXX-DAG: define linkonce_odr i32 @_Z29calledTransitivelyFromVarInitv() #{{[0-9]+}} comdat {
1920

2021
calledFromSwift()
22+
23+
let _ = varUsedFromSwift

0 commit comments

Comments
 (0)