Skip to content

Commit

Permalink
[InstallAPI] Collect global functions (#83952)
Browse files Browse the repository at this point in the history
* Include whether functions are inlinable as they impact whether to add
them into the tbd file and for future verification.
* Fix how clang arguments got passed along, previously spacing was
passed along to CC1 causing search path inputs to look non-existent.
  • Loading branch information
cyndyishida committed Mar 7, 2024
1 parent a9b0d75 commit 50ae8a2
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 11 deletions.
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

0 comments on commit 50ae8a2

Please sign in to comment.