| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| //===- bolt/Core/GDBIndex.cpp - GDB Index support ------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "bolt/Core/GDBIndex.h" | ||
|
|
||
| using namespace llvm::bolt; | ||
| using namespace llvm::support::endian; | ||
|
|
||
| void GDBIndex::addGDBTypeUnitEntry(const GDBIndexTUEntry &&Entry) { | ||
| std::lock_guard<std::mutex> Lock(GDBIndexMutex); | ||
| if (!BC.getGdbIndexSection()) | ||
| return; | ||
| GDBIndexTUEntryVector.emplace_back(Entry); | ||
| } | ||
|
|
||
| void GDBIndex::updateGdbIndexSection( | ||
| const CUOffsetMap &CUMap, const uint32_t NumCUs, | ||
| DebugARangesSectionWriter &ARangesSectionWriter) { | ||
| if (!BC.getGdbIndexSection()) | ||
| return; | ||
|
|
||
| // See https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html | ||
| // for .gdb_index section format. | ||
|
|
||
| StringRef GdbIndexContents = BC.getGdbIndexSection()->getContents(); | ||
|
|
||
| const char *Data = GdbIndexContents.data(); | ||
|
|
||
| // Parse the header. | ||
| const uint32_t Version = read32le(Data); | ||
| if (Version != 7 && Version != 8) { | ||
| errs() << "BOLT-ERROR: can only process .gdb_index versions 7 and 8\n"; | ||
| exit(1); | ||
| } | ||
|
|
||
| // Some .gdb_index generators use file offsets while others use section | ||
| // offsets. Hence we can only rely on offsets relative to each other, | ||
| // and ignore their absolute values. | ||
| const uint32_t CUListOffset = read32le(Data + 4); | ||
| const uint32_t CUTypesOffset = read32le(Data + 8); | ||
| const uint32_t AddressTableOffset = read32le(Data + 12); | ||
| const uint32_t SymbolTableOffset = read32le(Data + 16); | ||
| const uint32_t ConstantPoolOffset = read32le(Data + 20); | ||
| Data += 24; | ||
|
|
||
| // Map CUs offsets to indices and verify existing index table. | ||
| std::map<uint32_t, uint32_t> OffsetToIndexMap; | ||
| const uint32_t CUListSize = CUTypesOffset - CUListOffset; | ||
| const uint32_t TUListSize = AddressTableOffset - CUTypesOffset; | ||
| const unsigned NUmCUsEncoded = CUListSize / 16; | ||
| unsigned MaxDWARFVersion = BC.DwCtx->getMaxVersion(); | ||
| unsigned NumDWARF5TUs = | ||
| getGDBIndexTUEntryVector().size() - BC.DwCtx->getNumTypeUnits(); | ||
| bool SkipTypeUnits = false; | ||
| // For DWARF5 Types are in .debug_info. | ||
| // LLD doesn't generate Types CU List, and in CU list offset | ||
| // only includes CUs. | ||
| // GDB 11+ includes only CUs in CU list and generates Types | ||
| // list. | ||
| // GDB 9 includes CUs and TUs in CU list and generates TYpes | ||
| // list. The NumCUs is CUs + TUs, so need to modify the check. | ||
| // For split-dwarf | ||
| // GDB-11, DWARF5: TU units from dwo are not included. | ||
| // GDB-11, DWARF4: TU units from dwo are included. | ||
| if (MaxDWARFVersion >= 5) | ||
| SkipTypeUnits = !TUListSize ? true | ||
| : ((NUmCUsEncoded + NumDWARF5TUs) == | ||
| BC.DwCtx->getNumCompileUnits()); | ||
|
|
||
| if (!((CUListSize == NumCUs * 16) || | ||
| (CUListSize == (NumCUs + NumDWARF5TUs) * 16))) { | ||
| errs() << "BOLT-ERROR: .gdb_index: CU count mismatch\n"; | ||
| exit(1); | ||
| } | ||
| DenseSet<uint64_t> OriginalOffsets; | ||
| for (unsigned Index = 0, Units = BC.DwCtx->getNumCompileUnits(); | ||
| Index < Units; ++Index) { | ||
| const DWARFUnit *CU = BC.DwCtx->getUnitAtIndex(Index); | ||
| if (SkipTypeUnits && CU->isTypeUnit()) | ||
| continue; | ||
| const uint64_t Offset = read64le(Data); | ||
| Data += 16; | ||
| if (CU->getOffset() != Offset) { | ||
| errs() << "BOLT-ERROR: .gdb_index CU offset mismatch\n"; | ||
| exit(1); | ||
| } | ||
|
|
||
| OriginalOffsets.insert(Offset); | ||
| OffsetToIndexMap[Offset] = Index; | ||
| } | ||
|
|
||
| // Ignore old address table. | ||
| const uint32_t OldAddressTableSize = SymbolTableOffset - AddressTableOffset; | ||
| // Move Data to the beginning of symbol table. | ||
| Data += SymbolTableOffset - CUTypesOffset; | ||
|
|
||
| // Calculate the size of the new address table. | ||
| uint32_t NewAddressTableSize = 0; | ||
| for (const auto &CURangesPair : ARangesSectionWriter.getCUAddressRanges()) { | ||
| const SmallVector<DebugAddressRange, 2> &Ranges = CURangesPair.second; | ||
| NewAddressTableSize += Ranges.size() * 20; | ||
| } | ||
|
|
||
| // Difference between old and new table (and section) sizes. | ||
| // Could be negative. | ||
| int32_t Delta = NewAddressTableSize - OldAddressTableSize; | ||
|
|
||
| size_t NewGdbIndexSize = GdbIndexContents.size() + Delta; | ||
|
|
||
| // Free'd by ExecutableFileMemoryManager. | ||
| auto *NewGdbIndexContents = new uint8_t[NewGdbIndexSize]; | ||
| uint8_t *Buffer = NewGdbIndexContents; | ||
|
|
||
| write32le(Buffer, Version); | ||
| write32le(Buffer + 4, CUListOffset); | ||
| write32le(Buffer + 8, CUTypesOffset); | ||
| write32le(Buffer + 12, AddressTableOffset); | ||
| write32le(Buffer + 16, SymbolTableOffset + Delta); | ||
| write32le(Buffer + 20, ConstantPoolOffset + Delta); | ||
| Buffer += 24; | ||
|
|
||
| using MapEntry = std::pair<uint32_t, CUInfo>; | ||
| std::vector<MapEntry> CUVector(CUMap.begin(), CUMap.end()); | ||
| // Need to sort since we write out all of TUs in .debug_info before CUs. | ||
| std::sort(CUVector.begin(), CUVector.end(), | ||
| [](const MapEntry &E1, const MapEntry &E2) -> bool { | ||
| return E1.second.Offset < E2.second.Offset; | ||
| }); | ||
| // Writing out CU List <Offset, Size> | ||
| for (auto &CUInfo : CUVector) { | ||
| // Skipping TU for DWARF5 when they are not included in CU list. | ||
| if (!OriginalOffsets.count(CUInfo.first)) | ||
| continue; | ||
| write64le(Buffer, CUInfo.second.Offset); | ||
| // Length encoded in CU doesn't contain first 4 bytes that encode length. | ||
| write64le(Buffer + 8, CUInfo.second.Length + 4); | ||
| Buffer += 16; | ||
| } | ||
|
|
||
| // Rewrite TU CU List, since abbrevs can be different. | ||
| // Entry example: | ||
| // 0: offset = 0x00000000, type_offset = 0x0000001e, type_signature = | ||
| // 0x418503b8111e9a7b Spec says " triplet, the first value is the CU offset, | ||
| // the second value is the type offset in the CU, and the third value is the | ||
| // type signature" Looking at what is being generated by gdb-add-index. The | ||
| // first entry is TU offset, second entry is offset from it, and third entry | ||
| // is the type signature. | ||
| if (TUListSize) | ||
| for (const GDBIndexTUEntry &Entry : getGDBIndexTUEntryVector()) { | ||
| write64le(Buffer, Entry.UnitOffset); | ||
| write64le(Buffer + 8, Entry.TypeDIERelativeOffset); | ||
| write64le(Buffer + 16, Entry.TypeHash); | ||
| Buffer += sizeof(GDBIndexTUEntry); | ||
| } | ||
|
|
||
| // Generate new address table. | ||
| for (const std::pair<const uint64_t, DebugAddressRangesVector> &CURangesPair : | ||
| ARangesSectionWriter.getCUAddressRanges()) { | ||
| const uint32_t CUIndex = OffsetToIndexMap[CURangesPair.first]; | ||
| const DebugAddressRangesVector &Ranges = CURangesPair.second; | ||
| for (const DebugAddressRange &Range : Ranges) { | ||
| write64le(Buffer, Range.LowPC); | ||
| write64le(Buffer + 8, Range.HighPC); | ||
| write32le(Buffer + 16, CUIndex); | ||
| Buffer += 20; | ||
| } | ||
| } | ||
|
|
||
| const size_t TrailingSize = | ||
| GdbIndexContents.data() + GdbIndexContents.size() - Data; | ||
| assert(Buffer + TrailingSize == NewGdbIndexContents + NewGdbIndexSize && | ||
| "size calculation error"); | ||
|
|
||
| // Copy over the rest of the original data. | ||
| memcpy(Buffer, Data, TrailingSize); | ||
|
|
||
| // Register the new section. | ||
| BC.registerOrUpdateNoteSection(".gdb_index", NewGdbIndexContents, | ||
| NewGdbIndexSize); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| //===--- UseInternalLinkageCheck.cpp - clang-tidy--------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "UseInternalLinkageCheck.h" | ||
| #include "../utils/FileExtensionsUtils.h" | ||
| #include "clang/AST/Decl.h" | ||
| #include "clang/ASTMatchers/ASTMatchFinder.h" | ||
| #include "clang/ASTMatchers/ASTMatchers.h" | ||
| #include "clang/ASTMatchers/ASTMatchersMacros.h" | ||
| #include "clang/Basic/SourceLocation.h" | ||
| #include "clang/Basic/Specifiers.h" | ||
| #include "llvm/ADT/STLExtras.h" | ||
|
|
||
| using namespace clang::ast_matchers; | ||
|
|
||
| namespace clang::tidy::misc { | ||
|
|
||
| namespace { | ||
|
|
||
| AST_MATCHER(Decl, isFirstDecl) { return Node.isFirstDecl(); } | ||
|
|
||
| static bool isInMainFile(SourceLocation L, SourceManager &SM, | ||
| const FileExtensionsSet &HeaderFileExtensions) { | ||
| for (;;) { | ||
| if (utils::isSpellingLocInHeaderFile(L, SM, HeaderFileExtensions)) | ||
| return false; | ||
| if (SM.isInMainFile(L)) | ||
| return true; | ||
| // not in header file but not in main file | ||
| L = SM.getIncludeLoc(SM.getFileID(L)); | ||
| if (L.isValid()) | ||
| continue; | ||
| // Conservative about the unknown | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| AST_MATCHER_P(Decl, isAllRedeclsInMainFile, FileExtensionsSet, | ||
| HeaderFileExtensions) { | ||
| return llvm::all_of(Node.redecls(), [&](const Decl *D) { | ||
| return isInMainFile(D->getLocation(), | ||
| Finder->getASTContext().getSourceManager(), | ||
| HeaderFileExtensions); | ||
| }); | ||
| } | ||
|
|
||
| AST_POLYMORPHIC_MATCHER(isExternStorageClass, | ||
| AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, | ||
| VarDecl)) { | ||
| return Node.getStorageClass() == SC_Extern; | ||
| } | ||
|
|
||
| } // namespace | ||
|
|
||
| void UseInternalLinkageCheck::registerMatchers(MatchFinder *Finder) { | ||
| auto Common = | ||
| allOf(isFirstDecl(), isAllRedeclsInMainFile(HeaderFileExtensions), | ||
| unless(anyOf( | ||
| // 1. internal linkage | ||
| isStaticStorageClass(), isInAnonymousNamespace(), | ||
| // 2. explicit external linkage | ||
| isExternStorageClass(), isExternC(), | ||
| // 3. template | ||
| isExplicitTemplateSpecialization(), | ||
| // 4. friend | ||
| hasAncestor(friendDecl())))); | ||
| Finder->addMatcher( | ||
| functionDecl(Common, unless(cxxMethodDecl()), unless(isMain())) | ||
| .bind("fn"), | ||
| this); | ||
| Finder->addMatcher(varDecl(Common, hasGlobalStorage()).bind("var"), this); | ||
| } | ||
|
|
||
| static constexpr StringRef Message = | ||
| "%0 %1 can be made static or moved into an anonymous namespace " | ||
| "to enforce internal linkage"; | ||
|
|
||
| void UseInternalLinkageCheck::check(const MatchFinder::MatchResult &Result) { | ||
| if (const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("fn")) { | ||
| diag(FD->getLocation(), Message) << "function" << FD; | ||
| return; | ||
| } | ||
| if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var")) { | ||
| diag(VD->getLocation(), Message) << "variable" << VD; | ||
| return; | ||
| } | ||
| llvm_unreachable(""); | ||
| } | ||
|
|
||
| } // namespace clang::tidy::misc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| //===--- UseInternalLinkageCheck.h - clang-tidy -----------------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEINTERNALLINKAGECHECK_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEINTERNALLINKAGECHECK_H | ||
|
|
||
| #include "../ClangTidyCheck.h" | ||
|
|
||
| namespace clang::tidy::misc { | ||
|
|
||
| /// Detects variables and functions that can be marked as static or moved into | ||
| /// an anonymous namespace to enforce internal linkage. | ||
| /// | ||
| /// For the user-facing documentation see: | ||
| /// http://clang.llvm.org/extra/clang-tidy/checks/misc/use-internal-linkage.html | ||
| class UseInternalLinkageCheck : public ClangTidyCheck { | ||
| public: | ||
| UseInternalLinkageCheck(StringRef Name, ClangTidyContext *Context) | ||
| : ClangTidyCheck(Name, Context), | ||
| HeaderFileExtensions(Context->getHeaderFileExtensions()) {} | ||
| void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
| void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
| std::optional<TraversalKind> getCheckTraversalKind() const override { | ||
| return TK_IgnoreUnlessSpelledInSource; | ||
| } | ||
|
|
||
| private: | ||
| FileExtensionsSet HeaderFileExtensions; | ||
| }; | ||
|
|
||
| } // namespace clang::tidy::misc | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEINTERNALLINKAGECHECK_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| .. title:: clang-tidy - misc-use-internal-linkage | ||
|
|
||
| misc-use-internal-linkage | ||
| ========================= | ||
|
|
||
| Detects variables and functions that can be marked as static or moved into | ||
| an anonymous namespace to enforce internal linkage. | ||
|
|
||
| Static functions and variables are scoped to a single file. Marking functions | ||
| and variables as static helps to better remove dead code. In addition, it gives | ||
| the compiler more information and allows for more aggressive optimizations. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| int v1; // can be marked as static | ||
|
|
||
| void fn1(); // can be marked as static | ||
|
|
||
| namespace { | ||
| // already in anonymous namespace | ||
| int v2; | ||
| void fn2(); | ||
| } | ||
| // already declared as extern | ||
| extern int v2; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| #pragma once | ||
|
|
||
| void func_header(); | ||
|
|
||
| #include "func_h.inc" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| void func_cpp_inc(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| void func_h_inc(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| #pragma once | ||
|
|
||
| extern int gloabl_header; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| // RUN: not clang-tidy %s -checks='-*,misc-header-include-cycle' | ||
|
|
||
| #include "header-include-cycle.self.cpp" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| // RUN: %check_clang_tidy %s misc-use-internal-linkage %t -- -- -I%S/Inputs/use-internal-linkage | ||
|
|
||
| #include "func.h" | ||
|
|
||
| void func() {} | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'func' | ||
|
|
||
| template<class T> | ||
| void func_template() {} | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'func_template' | ||
|
|
||
| void func_cpp_inc(); | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'func_cpp_inc' | ||
|
|
||
| #include "func_cpp.inc" | ||
|
|
||
| void func_h_inc(); | ||
|
|
||
| struct S { | ||
| void method(); | ||
| }; | ||
| void S::method() {} | ||
|
|
||
| void func_header(); | ||
| extern void func_extern(); | ||
| static void func_static(); | ||
| namespace { | ||
| void func_anonymous_ns(); | ||
| } // namespace | ||
|
|
||
| int main(int argc, const char*argv[]) {} | ||
|
|
||
| extern "C" { | ||
| void func_extern_c_1() {} | ||
| } | ||
|
|
||
| extern "C" void func_extern_c_2() {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| // RUN: %check_clang_tidy %s misc-use-internal-linkage %t -- -- -I%S/Inputs/use-internal-linkage | ||
|
|
||
| #include "var.h" | ||
|
|
||
| int global; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: variable 'global' | ||
|
|
||
| template<class T> | ||
| T global_template; | ||
| // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: variable 'global_template' | ||
|
|
||
| int gloabl_header; | ||
|
|
||
| extern int global_extern; | ||
|
|
||
| static int global_static; | ||
|
|
||
| namespace { | ||
| static int global_anonymous_ns; | ||
| namespace NS { | ||
| static int global_anonymous_ns; | ||
| } | ||
| } | ||
|
|
||
| static void f(int para) { | ||
| int local; | ||
| static int local_static; | ||
| } | ||
|
|
||
| struct S { | ||
| int m1; | ||
| static int m2; | ||
| }; | ||
| int S::m2; | ||
|
|
||
| extern "C" { | ||
| int global_in_extern_c_1; | ||
| } | ||
|
|
||
| extern "C" int global_in_extern_c_2; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| // RUN: %check_clang_tidy -std=c++20 %s readability-implicit-bool-conversion %t | ||
|
|
||
| namespace std { | ||
| struct strong_ordering { | ||
| int n; | ||
| constexpr operator int() const { return n; } | ||
| static const strong_ordering equal, greater, less; | ||
| }; | ||
| constexpr strong_ordering strong_ordering::equal = {0}; | ||
| constexpr strong_ordering strong_ordering::greater = {1}; | ||
| constexpr strong_ordering strong_ordering::less = {-1}; | ||
| } // namespace std | ||
|
|
||
| namespace PR93409 { | ||
| struct X | ||
| { | ||
| auto operator<=>(const X&) const = default; | ||
| bool m_b; | ||
| }; | ||
|
|
||
| struct Y | ||
| { | ||
| auto operator<=>(const Y&) const = default; | ||
| X m_x; | ||
| }; | ||
|
|
||
| bool compare(const Y& y1, const Y& y2) | ||
| { | ||
| return y1 == y2 || y1 < y2 || y1 > y2; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -124,6 +124,7 @@ enum class CudaArch { | |
| GFX1103, | ||
| GFX1150, | ||
| GFX1151, | ||
| GFX1152, | ||
| GFX12_GENERIC, | ||
| GFX1200, | ||
| GFX1201, | ||
|
|
||