[clang] [llvm] [InstallAPI] Collect global functions (PR #83952)
Cyndy Ishida via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 5 07:47:36 PST 2024
https://github.com/cyndyishida updated https://github.com/llvm/llvm-project/pull/83952
>From 580af4d537446637d44af03c147750af54eca05b Mon Sep 17 00:00:00 2001
From: Cyndy Ishida <cyndy_ishida at apple.com>
Date: Mon, 4 Mar 2024 10:46:18 -0800
Subject: [PATCH] [InstallAPI] Collect global functions
* Include whether functions are inlinable as they impact whether to add
them into the tbd file and future verification.
* Fix how clang arguments got passed along, previously spacing was
passed along to CC1 causing search paths to look non-existent.
---
clang/include/clang/InstallAPI/Frontend.h | 5 +-
clang/include/clang/InstallAPI/Visitor.h | 3 +
clang/lib/InstallAPI/Frontend.cpp | 2 +-
clang/lib/InstallAPI/Visitor.cpp | 78 +++++++++++++++++++++++
clang/test/InstallAPI/functions.test | 78 +++++++++++++++++++++++
clang/tools/clang-installapi/Options.cpp | 4 +-
llvm/include/llvm/TextAPI/Record.h | 6 +-
llvm/include/llvm/TextAPI/RecordsSlice.h | 5 +-
llvm/lib/TextAPI/RecordsSlice.cpp | 6 +-
llvm/unittests/TextAPI/RecordTests.cpp | 3 +-
10 files changed, 180 insertions(+), 10 deletions(-)
create mode 100644 clang/test/InstallAPI/functions.test
diff --git a/clang/include/clang/InstallAPI/Frontend.h b/clang/include/clang/InstallAPI/Frontend.h
index 8774321e990c13..cbc2b159ebd17a 100644
--- a/clang/include/clang/InstallAPI/Frontend.h
+++ b/clang/include/clang/InstallAPI/Frontend.h
@@ -50,12 +50,15 @@ class FrontendRecordsSlice : public llvm::MachO::RecordsSlice {
/// \param D The pointer to the declaration from traversing AST.
/// \param Access The intended access level of symbol.
/// \param Flags The flags that describe attributes of the symbol.
+ /// \param Inlined Whether declaration is inlined, only applicable to
+ /// functions.
/// \return The non-owning pointer to added record in slice.
GlobalRecord *addGlobal(StringRef Name, RecordLinkage Linkage,
GlobalRecord::Kind GV,
const clang::AvailabilityInfo Avail, const Decl *D,
const HeaderType Access,
- SymbolFlags Flags = SymbolFlags::None);
+ SymbolFlags Flags = SymbolFlags::None,
+ bool Inlined = false);
/// Add ObjC Class record with attributes from AST.
///
diff --git a/clang/include/clang/InstallAPI/Visitor.h b/clang/include/clang/InstallAPI/Visitor.h
index ff0a9957aa86bc..71d4d9894f4205 100644
--- a/clang/include/clang/InstallAPI/Visitor.h
+++ b/clang/include/clang/InstallAPI/Visitor.h
@@ -37,6 +37,9 @@ class InstallAPIVisitor final : public ASTConsumer,
/// Collect global variables.
bool VisitVarDecl(const VarDecl *D);
+ /// Collect global functions.
+ bool VisitFunctionDecl(const FunctionDecl *D);
+
/// Collect Objective-C Interface declarations.
/// Every Objective-C class has an interface declaration that lists all the
/// ivars, properties, and methods of the class.
diff --git a/clang/lib/InstallAPI/Frontend.cpp b/clang/lib/InstallAPI/Frontend.cpp
index 240a80e1d3d82c..efc634d80dd218 100644
--- a/clang/lib/InstallAPI/Frontend.cpp
+++ b/clang/lib/InstallAPI/Frontend.cpp
@@ -19,7 +19,7 @@ namespace clang::installapi {
GlobalRecord *FrontendRecordsSlice::addGlobal(
StringRef Name, RecordLinkage Linkage, GlobalRecord::Kind GV,
const clang::AvailabilityInfo Avail, const Decl *D, const HeaderType Access,
- SymbolFlags Flags) {
+ SymbolFlags Flags, bool Inlined) {
auto *GR = llvm::MachO::RecordsSlice::addGlobal(Name, Linkage, GV, Flags);
FrontendRecords.insert({GR, FrontendAttrs{Avail, D, Access}});
diff --git a/clang/lib/InstallAPI/Visitor.cpp b/clang/lib/InstallAPI/Visitor.cpp
index fbe6f1dabe005d..1f2ef08e5aa252 100644
--- a/clang/lib/InstallAPI/Visitor.cpp
+++ b/clang/lib/InstallAPI/Visitor.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/InstallAPI/Visitor.h"
+#include "clang/AST/ParentMapContext.h"
#include "clang/Basic/Linkage.h"
#include "clang/InstallAPI/Frontend.h"
#include "llvm/ADT/SmallString.h"
@@ -27,6 +28,31 @@ static bool isExported(const NamedDecl *D) {
(LV.getVisibility() == DefaultVisibility);
}
+static bool isInlined(const FunctionDecl *D) {
+ bool HasInlineAttribute = false;
+ bool NoCXXAttr =
+ (!D->getASTContext().getLangOpts().CPlusPlus &&
+ !D->getASTContext().getTargetInfo().getCXXABI().isMicrosoft() &&
+ !D->hasAttr<DLLExportAttr>());
+
+ // Check all redeclarations to find an inline attribute or keyword.
+ for (const auto *RD : D->redecls()) {
+ if (!RD->isInlined())
+ continue;
+ HasInlineAttribute = true;
+ if (!(NoCXXAttr || RD->hasAttr<GNUInlineAttr>()))
+ continue;
+ if (RD->doesThisDeclarationHaveABody() &&
+ RD->isInlineDefinitionExternallyVisible())
+ return false;
+ }
+
+ if (!HasInlineAttribute)
+ return false;
+
+ return true;
+}
+
static SymbolFlags getFlags(bool WeakDef, bool ThreadLocal) {
SymbolFlags Result = SymbolFlags::None;
if (WeakDef)
@@ -204,4 +230,56 @@ bool InstallAPIVisitor::VisitVarDecl(const VarDecl *D) {
return true;
}
+bool InstallAPIVisitor::VisitFunctionDecl(const FunctionDecl *D) {
+ if (const CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(D)) {
+ // Skip member function in class templates.
+ if (M->getParent()->getDescribedClassTemplate() != nullptr)
+ return true;
+
+ // Skip methods in CXX RecordDecls.
+ for (auto P : D->getASTContext().getParents(*M)) {
+ if (P.get<CXXRecordDecl>())
+ return true;
+ }
+
+ // Skip CXX ConstructorDecls and DestructorDecls.
+ if (isa<CXXConstructorDecl>(M) || isa<CXXDestructorDecl>(M))
+ return true;
+ }
+
+ // Skip templated functions.
+ switch (D->getTemplatedKind()) {
+ case FunctionDecl::TK_NonTemplate:
+ case FunctionDecl::TK_DependentNonTemplate:
+ break;
+ case FunctionDecl::TK_MemberSpecialization:
+ case FunctionDecl::TK_FunctionTemplateSpecialization:
+ if (auto *TempInfo = D->getTemplateSpecializationInfo()) {
+ if (!TempInfo->isExplicitInstantiationOrSpecialization())
+ return true;
+ }
+ break;
+ case FunctionDecl::TK_FunctionTemplate:
+ case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
+ return true;
+ }
+
+ auto Access = getAccessForDecl(D);
+ if (!Access)
+ return true;
+ auto Name = getMangledName(D);
+ const AvailabilityInfo Avail = AvailabilityInfo::createFromDecl(D);
+ const bool ExplicitInstantiation = D->getTemplateSpecializationKind() ==
+ TSK_ExplicitInstantiationDeclaration;
+ const bool WeakDef = ExplicitInstantiation || D->hasAttr<WeakAttr>();
+ const bool Inlined = isInlined(D);
+ const RecordLinkage Linkage = (Inlined || !isExported(D))
+ ? RecordLinkage::Internal
+ : RecordLinkage::Exported;
+ Ctx.Slice->addGlobal(Name, Linkage, GlobalRecord::Kind::Function, Avail, D,
+ *Access, getFlags(WeakDef, /*ThreadLocal=*/false),
+ Inlined);
+ return true;
+}
+
} // namespace clang::installapi
diff --git a/clang/test/InstallAPI/functions.test b/clang/test/InstallAPI/functions.test
new file mode 100644
index 00000000000000..527965303cb351
--- /dev/null
+++ b/clang/test/InstallAPI/functions.test
@@ -0,0 +1,78 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: sed -e "s|DSTROOT|%/t|g" %t/inputs.json.in > %t/inputs.json
+
+// RUN: clang-installapi -target arm64-apple-macos13.1 \
+// RUN: -I%t/usr/include -I%t/usr/local/include \
+// RUN: -install_name @rpath/lib/libfunctions.dylib \
+// RUN: %t/inputs.json -o %t/outputs.tbd 2>&1 | FileCheck %s --allow-empty
+// RUN: llvm-readtapi -compare %t/outputs.tbd %t/expected.tbd 2>&1 | FileCheck %s --allow-empty
+
+// CHECK-NOT: error:
+// CHECK-NOT: warning:
+
+//--- usr/include/functions.h
+inline int inlined_func(void) { return 1;}
+int public(int a);
+
+//--- usr/local/include/private_functions.h
+__attribute__((visibility("hidden")))
+void hidden(void);
+
+//--- inputs.json.in
+{
+ "headers": [ {
+ "path" : "DSTROOT/usr/include/functions.h",
+ "type" : "public"
+ },
+ {
+ "path" : "DSTROOT/usr/local/include/private_functions.h",
+ "type" : "private"
+ }
+ ],
+ "version": "3"
+}
+
+//--- expected.tbd
+{
+ "main_library": {
+ "compatibility_versions": [
+ {
+ "version": "0"
+ }
+ ],
+ "current_versions": [
+ {
+ "version": "0"
+ }
+ ],
+ "exported_symbols": [
+ {
+ "text": {
+ "global": [
+ "_public"
+ ]
+ }
+ }
+ ],
+ "flags": [
+ {
+ "attributes": [
+ "not_app_extension_safe"
+ ]
+ }
+ ],
+ "install_names": [
+ {
+ "name": "@rpath/lib/libfunctions.dylib"
+ }
+ ],
+ "target_info": [
+ {
+ "min_deployment": "13.1",
+ "target": "arm64-macos"
+ }
+ ]
+ },
+ "tapi_tbd_version": 5
+}
diff --git a/clang/tools/clang-installapi/Options.cpp b/clang/tools/clang-installapi/Options.cpp
index 7d45e999448d9f..b9c36eab2ad3b7 100644
--- a/clang/tools/clang-installapi/Options.cpp
+++ b/clang/tools/clang-installapi/Options.cpp
@@ -112,7 +112,9 @@ Options::Options(DiagnosticsEngine &Diag, FileManager *FM,
for (const Arg *A : ArgList) {
if (A->isClaimed())
continue;
- FrontendArgs.emplace_back(A->getAsString(ArgList));
+
+ FrontendArgs.emplace_back(A->getSpelling());
+ llvm::copy(A->getValues(), std::back_inserter(FrontendArgs));
}
FrontendArgs.push_back("-fsyntax-only");
}
diff --git a/llvm/include/llvm/TextAPI/Record.h b/llvm/include/llvm/TextAPI/Record.h
index 867d6a23588326..98639b064eaadd 100644
--- a/llvm/include/llvm/TextAPI/Record.h
+++ b/llvm/include/llvm/TextAPI/Record.h
@@ -103,8 +103,8 @@ class GlobalRecord : public Record {
};
GlobalRecord(StringRef Name, RecordLinkage Linkage, SymbolFlags Flags,
- Kind GV)
- : Record({Name, Linkage, Flags}), GV(GV) {}
+ Kind GV, bool Inlined)
+ : Record({Name, Linkage, Flags}), GV(GV), Inlined(Inlined) {}
bool isFunction() const { return GV == Kind::Function; }
bool isVariable() const { return GV == Kind::Variable; }
@@ -112,9 +112,11 @@ class GlobalRecord : public Record {
if (GV == Kind::Unknown)
GV = V;
}
+ bool isInlined() const { return Inlined; }
private:
Kind GV;
+ bool Inlined = false;
};
// Define Objective-C instance variable records.
diff --git a/llvm/include/llvm/TextAPI/RecordsSlice.h b/llvm/include/llvm/TextAPI/RecordsSlice.h
index 57b23e5ea29e71..f934cf7607f1fd 100644
--- a/llvm/include/llvm/TextAPI/RecordsSlice.h
+++ b/llvm/include/llvm/TextAPI/RecordsSlice.h
@@ -53,10 +53,13 @@ class RecordsSlice {
/// \param Linkage The linkage of symbol.
/// \param GV The kind of global.
/// \param Flags The flags that describe attributes of the symbol.
+ /// \param Inlined Whether declaration is inlined, only applicable to
+ /// functions.
/// \return The non-owning pointer to added record in slice.
GlobalRecord *addGlobal(StringRef Name, RecordLinkage Linkage,
GlobalRecord::Kind GV,
- SymbolFlags Flags = SymbolFlags::None);
+ SymbolFlags Flags = SymbolFlags::None,
+ bool Inlined = false);
/// Add ObjC Class record.
///
diff --git a/llvm/lib/TextAPI/RecordsSlice.cpp b/llvm/lib/TextAPI/RecordsSlice.cpp
index db52a2cdd85c9c..111a1fa6eaf43b 100644
--- a/llvm/lib/TextAPI/RecordsSlice.cpp
+++ b/llvm/lib/TextAPI/RecordsSlice.cpp
@@ -171,8 +171,8 @@ ObjCIVarRecord *RecordsSlice::findObjCIVar(bool IsScopedName,
}
GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage,
- GlobalRecord::Kind GV,
- SymbolFlags Flags) {
+ GlobalRecord::Kind GV, SymbolFlags Flags,
+ bool Inlined) {
if (GV == GlobalRecord::Kind::Function)
Flags |= SymbolFlags::Text;
else if (GV == GlobalRecord::Kind::Variable)
@@ -182,7 +182,7 @@ GlobalRecord *RecordsSlice::addGlobal(StringRef Name, RecordLinkage Linkage,
auto Result = Globals.insert({Name, nullptr});
if (Result.second)
Result.first->second =
- std::make_unique<GlobalRecord>(Name, Linkage, Flags, GV);
+ std::make_unique<GlobalRecord>(Name, Linkage, Flags, GV, Inlined);
else {
updateLinkage(Result.first->second.get(), Linkage);
updateFlags(Result.first->second.get(), Flags);
diff --git a/llvm/unittests/TextAPI/RecordTests.cpp b/llvm/unittests/TextAPI/RecordTests.cpp
index 37289eca1bdf6b..89ffbc4275e0d6 100644
--- a/llvm/unittests/TextAPI/RecordTests.cpp
+++ b/llvm/unittests/TextAPI/RecordTests.cpp
@@ -19,7 +19,7 @@ TEST(TAPIRecord, Simple) {
GlobalRecord API{"_sym", RecordLinkage::Rexported,
SymbolFlags::Rexported | SymbolFlags::Text |
SymbolFlags::ThreadLocalValue,
- GlobalRecord::Kind::Function};
+ GlobalRecord::Kind::Function, /*Inlined=*/false};
EXPECT_TRUE(API.isExported());
EXPECT_TRUE(API.isText());
EXPECT_TRUE(API.isRexported());
@@ -30,6 +30,7 @@ TEST(TAPIRecord, Simple) {
EXPECT_FALSE(API.isWeakDefined());
EXPECT_FALSE(API.isWeakReferenced());
EXPECT_FALSE(API.isVariable());
+ EXPECT_FALSE(API.isInlined());
}
TEST(TAPIRecord, SimpleObjC) {
More information about the cfe-commits
mailing list