Skip to content

[InstallAPI] Collect global functions #83952

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

Merged
merged 2 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion clang/include/clang/InstallAPI/Frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/InstallAPI/Visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/InstallAPI/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ 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);
auto *GR =
llvm::MachO::RecordsSlice::addGlobal(Name, Linkage, GV, Flags, Inlined);
FrontendRecords.insert({GR, FrontendAttrs{Avail, D, Access}});
return GR;
}
Expand Down
78 changes: 78 additions & 0 deletions clang/lib/InstallAPI/Visitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
Expand Down Expand Up @@ -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
78 changes: 78 additions & 0 deletions clang/test/InstallAPI/functions.test
Original file line number Diff line number Diff line change
@@ -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
}
4 changes: 3 additions & 1 deletion clang/tools/clang-installapi/Options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
6 changes: 4 additions & 2 deletions llvm/include/llvm/TextAPI/Record.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,20 @@ 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; }
void setKind(const Kind &V) {
if (GV == Kind::Unknown)
GV = V;
}
bool isInlined() const { return Inlined; }

private:
Kind GV;
bool Inlined = false;
};

// Define Objective-C instance variable records.
Expand Down
5 changes: 4 additions & 1 deletion llvm/include/llvm/TextAPI/RecordsSlice.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
6 changes: 3 additions & 3 deletions llvm/lib/TextAPI/RecordsSlice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion llvm/unittests/TextAPI/RecordTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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) {
Expand Down