From bb9eb198298099742c823dce11c5edacc9c48d4e Mon Sep 17 00:00:00 2001 From: Petr Hosek Date: Fri, 15 Jan 2021 01:14:37 -0800 Subject: [PATCH] Support for instrumenting only selected files or functions This change implements support for applying profile instrumentation only to selected files or functions. The implementation uses the sanitizer special case list format to select which files and functions to instrument, and relies on the new noprofile IR attribute to exclude functions from instrumentation. Differential Revision: https://reviews.llvm.org/D94820 --- clang/docs/ClangCommandLineReference.rst | 6 + clang/docs/UsersManual.rst | 59 +++++++++ clang/include/clang/AST/ASTContext.h | 7 ++ clang/include/clang/Basic/LangOptions.h | 4 + clang/include/clang/Basic/ProfileList.h | 58 +++++++++ clang/include/clang/Driver/Options.td | 4 + clang/lib/AST/ASTContext.cpp | 1 + clang/lib/Basic/CMakeLists.txt | 1 + clang/lib/Basic/ProfileList.cpp | 113 ++++++++++++++++++ clang/lib/CodeGen/CodeGenFunction.cpp | 4 + clang/lib/CodeGen/CodeGenFunction.h | 3 +- clang/lib/CodeGen/CodeGenModule.cpp | 28 +++++ clang/lib/CodeGen/CodeGenModule.h | 4 + clang/lib/CodeGen/CodeGenPGO.cpp | 3 + clang/lib/Driver/ToolChains/Clang.cpp | 8 ++ clang/lib/Frontend/CompilerInvocation.cpp | 4 + clang/test/CodeGen/profile-filter.c | 56 +++++++++ llvm/include/llvm/Bitcode/LLVMBitCodes.h | 1 + llvm/include/llvm/IR/Attributes.td | 3 + llvm/lib/AsmParser/LLLexer.cpp | 1 + llvm/lib/AsmParser/LLParser.cpp | 3 + llvm/lib/AsmParser/LLToken.h | 1 + llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 2 + llvm/lib/IR/Attributes.cpp | 2 + llvm/lib/IR/Verifier.cpp | 1 + .../Instrumentation/PGOInstrumentation.cpp | 2 + llvm/lib/Transforms/Utils/CodeExtractor.cpp | 1 + llvm/test/Transforms/PGOProfile/noprofile.ll | 25 ++++ 28 files changed, 404 insertions(+), 1 deletion(-) create mode 100644 clang/include/clang/Basic/ProfileList.h create mode 100644 clang/lib/Basic/ProfileList.cpp create mode 100644 clang/test/CodeGen/profile-filter.c create mode 100644 llvm/test/Transforms/PGOProfile/noprofile.ll diff --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst index fc42bcfe3759..0ec99315a130 100644 --- a/clang/docs/ClangCommandLineReference.rst +++ b/clang/docs/ClangCommandLineReference.rst @@ -2045,6 +2045,12 @@ Set update method of profile counters (atomic,prefer-atomic,single) Use instrumentation data for profile-guided optimization. If pathname is a directory, it reads from /default.profdata. Otherwise, it reads from file . +.. program:: clang1 +.. option:: -fprofile-list= +.. program:: clang + +Filename defining the list of functions/files to instrument. The file uses the sanitizer special case list format. + .. option:: -freciprocal-math, -fno-reciprocal-math Allow division operations to be reassociated diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index fe944a0166b8..f7301204a350 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2282,6 +2282,63 @@ In these cases, you can use the flag ``-fno-profile-instr-generate`` (or Note that these flags should appear after the corresponding profile flags to have an effect. +Instrumenting only selected files or functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sometimes it's useful to only instrument certain files or functions. For +example in automated testing infrastructure, it may be desirable to only +instrument files or functions that were modified by a patch to reduce the +overhead of instrumenting a full system. + +This can be done using the ``-fprofile-list`` option. + +.. option:: -fprofile-list= + + This option can be used to apply profile instrumentation only to selected + files or functions. ``pathname`` should point to a file in the + :doc:`SanitizerSpecialCaseList` format which selects which files and + functions to instrument. + + .. code-block:: console + + $ echo "fun:test" > fun.list + $ clang++ -O2 -fprofile-instr-generate -fprofile-list=fun.list code.cc -o code + +The option can be specified multiple times to pass multiple files. + +.. code-block:: console + + $ echo "!fun:*test*" > fun.list + $ echo "src:code.cc" > src.list + % clang++ -O2 -fprofile-instr-generate -fcoverage-mapping -fprofile-list=fun.list -fprofile-list=code.list code.cc -o code + +To filter individual functions or entire source files using ``fun:`` or +``src:`` respectively. To exclude a function or a source file, use +``!fun:`` or ``!src:`` respectively. The format also supports +wildcard expansion. The compiler generated functions are assumed to be located +in the main source file. It is also possible to restrict the filter to a +particular instrumentation type by using a named section. + +.. code-block:: none + + # all functions whose name starts with foo will be instrumented. + fun:foo* + + # except for foo1 which will be excluded from instrumentation. + !fun:foo1 + + # every function in path/to/foo.cc will be instrumented. + src:path/to/foo.cc + + # bar will be instrumented only when using backend instrumentation. + # Recognized section names are clang, llvm and csllvm. + [llvm] + fun:bar + +When the file contains only excludes, all files and functions except for the +excluded ones will be instrumented. Otherwise, only the files and functions +specified will be instrumented. + Profile remapping ^^^^^^^^^^^^^^^^^ @@ -3740,6 +3797,8 @@ Execute ``clang-cl /?`` to see a list of supported options: Use instrumentation data for profile-guided optimization -fprofile-remapping-file= Use the remappings described in to match the profile data against names in the program + -fprofile-list= + Filename defining the list of functions/files to instrument -fsanitize-address-field-padding= Level of field padding for AddressSanitizer -fsanitize-address-globals-dead-stripping diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index bc86022d3743..ce47d54e44b0 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -36,6 +36,7 @@ #include "clang/Basic/Linkage.h" #include "clang/Basic/OperatorKinds.h" #include "clang/Basic/PartialDiagnostic.h" +#include "clang/Basic/ProfileList.h" #include "clang/Basic/SanitizerBlacklist.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" @@ -566,6 +567,10 @@ private: /// should be imbued with the XRay "always" or "never" attributes. std::unique_ptr XRayFilter; + /// ProfileList object that is used by the profile instrumentation + /// to decide which entities should be instrumented. + std::unique_ptr ProfList; + /// The allocator used to create AST objects. /// /// AST objects are never destructed; rather, all memory associated with the @@ -691,6 +696,8 @@ public: return *XRayFilter; } + const ProfileList &getProfileList() const { return *ProfList; } + DiagnosticsEngine &getDiagnostics() const; FullSourceLoc getFullLoc(SourceLocation Loc) const { diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index e1d41c33e00f..d8bd2a8b52fc 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -285,6 +285,10 @@ public: /// attribute(s). std::vector XRayAttrListFiles; + /// Paths to special case list files specifying which entities + /// (files, functions) should or should not be instrumented. + std::vector ProfileListFiles; + clang::ObjCRuntime ObjCRuntime; CoreFoundationABI CFRuntime = CoreFoundationABI::Unspecified; diff --git a/clang/include/clang/Basic/ProfileList.h b/clang/include/clang/Basic/ProfileList.h new file mode 100644 index 000000000000..989c36549a3d --- /dev/null +++ b/clang/include/clang/Basic/ProfileList.h @@ -0,0 +1,58 @@ +//===--- ProfileList.h - ProfileList filter ---------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// User-provided filters include/exclude profile instrumentation in certain +// functions. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_BASIC_INSTRPROFLIST_H +#define LLVM_CLANG_BASIC_INSTRPROFLIST_H + +#include "clang/Basic/CodeGenOptions.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include + +namespace llvm { +class SpecialCaseList; +} + +namespace clang { + +class ProfileSpecialCaseList; + +class ProfileList { + std::unique_ptr SCL; + const bool Empty; + const bool Default; + SourceManager &SM; + +public: + ProfileList(ArrayRef Paths, SourceManager &SM); + ~ProfileList(); + + bool isEmpty() const { return Empty; } + bool getDefault() const { return Default; } + + llvm::Optional + isFunctionExcluded(StringRef FunctionName, + CodeGenOptions::ProfileInstrKind Kind) const; + llvm::Optional + isLocationExcluded(SourceLocation Loc, + CodeGenOptions::ProfileInstrKind Kind) const; + llvm::Optional + isFileExcluded(StringRef FileName, + CodeGenOptions::ProfileInstrKind Kind) const; +}; + +} // namespace clang + +#endif diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 736cf98920cf..42c5319041d0 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1151,6 +1151,10 @@ defm pseudo_probe_for_profiling : BoolFOption<"pseudo-probe-for-profiling", def forder_file_instrumentation : Flag<["-"], "forder-file-instrumentation">, Group, Flags<[CC1Option, CoreOption]>, HelpText<"Generate instrumented code to collect order file into default.profraw file (overridden by '=' form of option or LLVM_PROFILE_FILE env var)">; +def fprofile_list_EQ : Joined<["-"], "fprofile-list=">, + Group, Flags<[CC1Option, CoreOption]>, + HelpText<"Filename defining the list of functions/files to instrument">, + MarshallingInfoStringVector>; defm addrsig : BoolFOption<"addrsig", CodeGenOpts<"Addrsig">, DefaultFalse, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 44545f00b146..cb7f00abf9e9 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -965,6 +965,7 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, XRayFilter(new XRayFunctionFilter(LangOpts.XRayAlwaysInstrumentFiles, LangOpts.XRayNeverInstrumentFiles, LangOpts.XRayAttrListFiles, SM)), + ProfList(new ProfileList(LangOpts.ProfileListFiles, SM)), PrintingPolicy(LOpts), Idents(idents), Selectors(sels), BuiltinInfo(builtins), DeclarationNames(*this), Comments(SM), CommentCommandTraits(BumpAlloc, LOpts.CommentOpts), diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt index b1c5003e172a..e5319484393a 100644 --- a/clang/lib/Basic/CMakeLists.txt +++ b/clang/lib/Basic/CMakeLists.txt @@ -59,6 +59,7 @@ add_clang_library(clangBasic OpenCLOptions.cpp OpenMPKinds.cpp OperatorPrecedence.cpp + ProfileList.cpp SanitizerBlacklist.cpp SanitizerSpecialCaseList.cpp Sanitizers.cpp diff --git a/clang/lib/Basic/ProfileList.cpp b/clang/lib/Basic/ProfileList.cpp new file mode 100644 index 000000000000..56bc37a79301 --- /dev/null +++ b/clang/lib/Basic/ProfileList.cpp @@ -0,0 +1,113 @@ +//===--- ProfileList.h - ProfileList filter ---------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// User-provided filters include/exclude profile instrumentation in certain +// functions or files. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/ProfileList.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/SpecialCaseList.h" + +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +namespace clang { + +class ProfileSpecialCaseList : public llvm::SpecialCaseList { +public: + static std::unique_ptr + create(const std::vector &Paths, llvm::vfs::FileSystem &VFS, + std::string &Error); + + static std::unique_ptr + createOrDie(const std::vector &Paths, + llvm::vfs::FileSystem &VFS); + + bool isEmpty() const { return Sections.empty(); } + + bool hasPrefix(StringRef Prefix) const { + for (auto &SectionIter : Sections) + if (SectionIter.Entries.count(Prefix) > 0) + return true; + return false; + } +}; + +std::unique_ptr +ProfileSpecialCaseList::create(const std::vector &Paths, + llvm::vfs::FileSystem &VFS, + std::string &Error) { + auto PSCL = std::make_unique(); + if (PSCL->createInternal(Paths, VFS, Error)) + return PSCL; + return nullptr; +} + +std::unique_ptr +ProfileSpecialCaseList::createOrDie(const std::vector &Paths, + llvm::vfs::FileSystem &VFS) { + std::string Error; + if (auto PSCL = create(Paths, VFS, Error)) + return PSCL; + llvm::report_fatal_error(Error); +} + +} + +ProfileList::ProfileList(ArrayRef Paths, SourceManager &SM) + : SCL(ProfileSpecialCaseList::createOrDie( + Paths, SM.getFileManager().getVirtualFileSystem())), + Empty(SCL->isEmpty()), + Default(SCL->hasPrefix("fun") || SCL->hasPrefix("src")), SM(SM) {} + +ProfileList::~ProfileList() = default; + +static StringRef getSectionName(CodeGenOptions::ProfileInstrKind Kind) { + switch (Kind) { + case CodeGenOptions::ProfileNone: + return ""; + case CodeGenOptions::ProfileClangInstr: + return "clang"; + case CodeGenOptions::ProfileIRInstr: + return "llvm"; + case CodeGenOptions::ProfileCSIRInstr: + return "csllvm"; + } +} + +llvm::Optional +ProfileList::isFunctionExcluded(StringRef FunctionName, + CodeGenOptions::ProfileInstrKind Kind) const { + StringRef Section = getSectionName(Kind); + if (SCL->inSection(Section, "!fun", FunctionName)) + return true; + if (SCL->inSection(Section, "fun", FunctionName)) + return false; + return None; +} + +llvm::Optional +ProfileList::isLocationExcluded(SourceLocation Loc, + CodeGenOptions::ProfileInstrKind Kind) const { + return isFileExcluded(SM.getFilename(SM.getFileLoc(Loc)), Kind); +} + +llvm::Optional +ProfileList::isFileExcluded(StringRef FileName, + CodeGenOptions::ProfileInstrKind Kind) const { + StringRef Section = getSectionName(Kind); + if (SCL->inSection(Section, "!src", FileName)) + return true; + if (SCL->inSection(Section, "src", FileName)) + return false; + return None; +} diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 2c302ae48d61..b393c88f7751 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -839,6 +839,10 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, } } + if (CGM.getCodeGenOpts().getProfileInstr() != CodeGenOptions::ProfileNone) + if (CGM.isProfileInstrExcluded(Fn, Loc)) + Fn->addFnAttr(llvm::Attribute::NoProfile); + unsigned Count, Offset; if (const auto *Attr = D ? D->getAttr() : nullptr) { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 9d11466f69ce..8eb7adbc8fcb 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -1442,7 +1442,8 @@ public: /// Increment the profiler's counter for the given statement by \p StepV. /// If \p StepV is null, the default increment is 1. void incrementProfileCounter(const Stmt *S, llvm::Value *StepV = nullptr) { - if (CGM.getCodeGenOpts().hasProfileClangInstr()) + if (CGM.getCodeGenOpts().hasProfileClangInstr() && + !CurFn->hasFnAttribute(llvm::Attribute::NoProfile)) PGO.emitCounterIncrement(Builder, S, StepV); PGO.setCurrentStmt(S); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index d93c9690cfb2..31afbc6b4262 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2563,6 +2563,34 @@ bool CodeGenModule::imbueXRayAttrs(llvm::Function *Fn, SourceLocation Loc, return true; } +bool CodeGenModule::isProfileInstrExcluded(llvm::Function *Fn, + SourceLocation Loc) const { + const auto &ProfileList = getContext().getProfileList(); + // If the profile list is empty, then instrument everything. + if (ProfileList.isEmpty()) + return false; + CodeGenOptions::ProfileInstrKind Kind = getCodeGenOpts().getProfileInstr(); + // First, check the function name. + Optional V = ProfileList.isFunctionExcluded(Fn->getName(), Kind); + if (V.hasValue()) + return *V; + // Next, check the source location. + if (Loc.isValid()) { + Optional V = ProfileList.isLocationExcluded(Loc, Kind); + if (V.hasValue()) + return *V; + } + // If location is unknown, this may be a compiler-generated function. Assume + // it's located in the main file. + auto &SM = Context.getSourceManager(); + if (const auto *MainFile = SM.getFileEntryForID(SM.getMainFileID())) { + Optional V = ProfileList.isFileExcluded(MainFile->getName(), Kind); + if (V.hasValue()) + return *V; + } + return ProfileList.getDefault(); +} + bool CodeGenModule::MustBeEmitted(const ValueDecl *Global) { // Never defer when EmitAllDecls is specified. if (LangOpts.EmitAllDecls) diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index c59570598b0d..618e2f857b07 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1277,6 +1277,10 @@ public: bool imbueXRayAttrs(llvm::Function *Fn, SourceLocation Loc, StringRef Category = StringRef()) const; + /// Returns true if function at the given location should be excluded from + /// profile instrumentation. + bool isProfileInstrExcluded(llvm::Function *Fn, SourceLocation Loc) const; + SanitizerMetadata *getSanitizerMetadata() { return SanitizerMD.get(); } diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp index 052d58a04a14..08ae87785065 100644 --- a/clang/lib/CodeGen/CodeGenPGO.cpp +++ b/clang/lib/CodeGen/CodeGenPGO.cpp @@ -811,6 +811,9 @@ void CodeGenPGO::assignRegionCounters(GlobalDecl GD, llvm::Function *Fn) { if (isa(D) && GD.getDtorType() != Dtor_Base) return; + if (Fn->hasFnAttribute(llvm::Attribute::NoProfile)) + return; + CGM.ClearUnusedCoverageMapping(D); setFuncName(Fn); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index a1f0e2cb9b29..fdb8a58cd1b3 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5531,6 +5531,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, const XRayArgs &XRay = TC.getXRayArgs(); XRay.addArgs(TC, Args, CmdArgs, InputType); + for (const auto &Filename : + Args.getAllArgValues(options::OPT_fprofile_list_EQ)) { + if (D.getVFS().exists(Filename)) + CmdArgs.push_back(Args.MakeArgString("-fprofile-list=" + Filename)); + else + D.Diag(clang::diag::err_drv_no_such_file) << Filename; + } + if (Arg *A = Args.getLastArg(options::OPT_fpatchable_function_entry_EQ)) { StringRef S0 = A->getValue(), S = S0; unsigned Size, Offset = 0; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index f7f36d04f33f..d8be4ea14868 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1358,6 +1358,10 @@ static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, } } + // -fprofile-list= dependencies. + for (const auto &Filename : Args.getAllArgValues(OPT_fprofile_list_EQ)) + Opts.ExtraDeps.push_back(Filename); + // Propagate the extra dependencies. for (const auto *A : Args.filtered(OPT_fdepfile_entry)) { Opts.ExtraDeps.push_back(A->getValue()); diff --git a/clang/test/CodeGen/profile-filter.c b/clang/test/CodeGen/profile-filter.c new file mode 100644 index 000000000000..5415ff96cb14 --- /dev/null +++ b/clang/test/CodeGen/profile-filter.c @@ -0,0 +1,56 @@ +// RUN: %clang_cc1 -fprofile-instrument=clang -emit-llvm %s -o - | FileCheck %s + +// RUN: echo "fun:test1" > %t-func.list +// RUN: %clang_cc1 -fprofile-instrument=clang -fprofile-list=%t-func.list -emit-llvm %s -o - | FileCheck %s --check-prefix=FUNC + +// RUN: echo "src:%s" | sed -e 's/\\/\\\\/g' > %t-file.list +// RUN: %clang_cc1 -fprofile-instrument=clang -fprofile-list=%t-file.list -emit-llvm %s -o - | FileCheck %s --check-prefix=FILE + +// RUN: echo -e "[clang]\nfun:test1\n[llvm]\nfun:test2" > %t-section.list +// RUN: %clang_cc1 -fprofile-instrument=llvm -fprofile-list=%t-section.list -emit-llvm %s -o - | FileCheck %s --check-prefix=SECTION + +// RUN: echo -e "fun:test*\n!fun:test1" > %t-exclude.list +// RUN: %clang_cc1 -fprofile-instrument=clang -fprofile-list=%t-exclude.list -emit-llvm %s -o - | FileCheck %s --check-prefix=EXCLUDE + +// RUN: echo "!fun:test1" > %t-exclude-only.list +// RUN: %clang_cc1 -fprofile-instrument=clang -fprofile-list=%t-exclude-only.list -emit-llvm %s -o - | FileCheck %s --check-prefix=EXCLUDE + +unsigned i; + +// CHECK-NOT: noprofile +// CHECK: @test1 +// FUNC-NOT: noprofile +// FUNC: @test1 +// FILE-NOT: noprofile +// FILE: @test1 +// SECTION: noprofile +// SECTION: @test1 +// EXCLUDE: noprofile +// EXCLUDE: @test1 +unsigned test1() { + // CHECK: %pgocount = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc_test1, i64 0, i64 0), align 8 + // FUNC: %pgocount = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc_test1, i64 0, i64 0), align 8 + // FILE: %pgocount = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc_test1, i64 0, i64 0), align 8 + // SECTION-NOT: %pgocount = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc_test1, i64 0, i64 0), align 8 + // EXCLUDE-NOT: %pgocount = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc_test1, i64 0, i64 0), align 8 + return i + 1; +} + +// CHECK-NOT: noprofile +// CHECK: @test2 +// FUNC: noprofile +// FUNC: @test2 +// FILE-NOT: noprofile +// FILE: @test2 +// SECTION-NOT: noprofile +// SECTION: @test2 +// EXCLUDE-NOT: noprofile +// EXCLUDE: @test2 +unsigned test2() { + // CHECK: %pgocount = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc_test2, i64 0, i64 0), align 8 + // FUNC-NOT: %pgocount = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc_test2, i64 0, i64 0), align 8 + // FILE: %pgocount = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc_test2, i64 0, i64 0), align 8 + // SECTION: %pgocount = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc_test2, i64 0, i64 0), align 8 + // EXCLUDE: %pgocount = load i64, i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc_test2, i64 0, i64 0), align 8 + return i - 1; +} diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index 03dac9ebede9..5b4854d6c95e 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -656,6 +656,7 @@ enum AttributeKindCodes { ATTR_KIND_MUSTPROGRESS = 70, ATTR_KIND_NO_CALLBACK = 71, ATTR_KIND_HOT = 72, + ATTR_KIND_NO_PROFILE = 73, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td index 45460742bf3f..f7ffc888c65a 100644 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -148,6 +148,9 @@ def NoSync : EnumAttr<"nosync">; /// Disable Indirect Branch Tracking. def NoCfCheck : EnumAttr<"nocf_check">; +/// Function should be instrumented. +def NoProfile : EnumAttr<"noprofile">; + /// Function doesn't unwind stack. def NoUnwind : EnumAttr<"nounwind">; diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index 3c35c9b1a5d9..427de74f91ac 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -663,6 +663,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(nonlazybind); KEYWORD(nomerge); KEYWORD(nonnull); + KEYWORD(noprofile); KEYWORD(noredzone); KEYWORD(noreturn); KEYWORD(nosync); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index eff81e36fed7..2a3fb8fb6658 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1368,6 +1368,7 @@ bool LLParser::parseFnAttributeValuePairs(AttrBuilder &B, case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); break; case lltok::kw_nosync: B.addAttribute(Attribute::NoSync); break; case lltok::kw_nocf_check: B.addAttribute(Attribute::NoCfCheck); break; + case lltok::kw_noprofile: B.addAttribute(Attribute::NoProfile); break; case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break; case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break; case lltok::kw_null_pointer_is_valid: @@ -1778,6 +1779,7 @@ bool LLParser::parseOptionalParamAttrs(AttrBuilder &B) { case lltok::kw_noinline: case lltok::kw_nonlazybind: case lltok::kw_nomerge: + case lltok::kw_noprofile: case lltok::kw_noredzone: case lltok::kw_noreturn: case lltok::kw_nocf_check: @@ -1886,6 +1888,7 @@ bool LLParser::parseOptionalReturnAttrs(AttrBuilder &B) { case lltok::kw_noinline: case lltok::kw_nonlazybind: case lltok::kw_nomerge: + case lltok::kw_noprofile: case lltok::kw_noredzone: case lltok::kw_noreturn: case lltok::kw_nocf_check: diff --git a/llvm/lib/AsmParser/LLToken.h b/llvm/lib/AsmParser/LLToken.h index 32e1165c7745..5149f861837a 100644 --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -210,6 +210,7 @@ enum Kind { kw_nonlazybind, kw_nomerge, kw_nonnull, + kw_noprofile, kw_noredzone, kw_noreturn, kw_nosync, diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index 4d886f708cd4..37ecb9992e44 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -680,6 +680,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_NOSYNC; case Attribute::NoCfCheck: return bitc::ATTR_KIND_NOCF_CHECK; + case Attribute::NoProfile: + return bitc::ATTR_KIND_NO_PROFILE; case Attribute::NoUnwind: return bitc::ATTR_KIND_NO_UNWIND; case Attribute::NullPointerIsValid: diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index b17673f14c9c..c4629decc6d9 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -403,6 +403,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const { return "nocf_check"; if (hasAttribute(Attribute::NoRecurse)) return "norecurse"; + if (hasAttribute(Attribute::NoProfile)) + return "noprofile"; if (hasAttribute(Attribute::NoUnwind)) return "nounwind"; if (hasAttribute(Attribute::OptForFuzzing)) diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 2b12e656c45e..100e881c8fa8 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1655,6 +1655,7 @@ static bool isFuncOnlyAttr(Attribute::AttrKind Kind) { case Attribute::StrictFP: case Attribute::NullPointerIsValid: case Attribute::MustProgress: + case Attribute::NoProfile: return true; default: break; diff --git a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp index 17ccf3ab989a..be6c8c631001 100644 --- a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp +++ b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp @@ -1591,6 +1591,8 @@ static bool InstrumentAllFunctions( for (auto &F : M) { if (F.isDeclaration()) continue; + if (F.hasFnAttribute(llvm::Attribute::NoProfile)) + continue; auto &TLI = LookupTLI(F); auto *BPI = LookupBPI(F); auto *BFI = LookupBFI(F); diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp index 2a7c62b235f5..390925a03b73 100644 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -973,6 +973,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs, case Attribute::UWTable: case Attribute::NoCfCheck: case Attribute::MustProgress: + case Attribute::NoProfile: break; } diff --git a/llvm/test/Transforms/PGOProfile/noprofile.ll b/llvm/test/Transforms/PGOProfile/noprofile.ll new file mode 100644 index 000000000000..d7df07b40437 --- /dev/null +++ b/llvm/test/Transforms/PGOProfile/noprofile.ll @@ -0,0 +1,25 @@ +; RUN: opt < %s -pgo-instr-gen -S | FileCheck %s +; RUN: opt < %s -passes=pgo-instr-gen -S | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@i = dso_local global i32 0, align 4 + +define i32 @test1() { +entry: +; CHECK: call void @llvm.instrprof.increment + %0 = load i32, i32* @i, align 4 + %add = add i32 %0, 1 + ret i32 %add +} + +define i32 @test2() #0 { +entry: +; CHECK-NOT: call void @llvm.instrprof.increment + %0 = load i32, i32* @i, align 4 + %sub = sub i32 %0, 1 + ret i32 %sub +} + +attributes #0 = { noprofile }