From 4edf35f11a9e20bd5df3cb47283715f0ff38b751 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/SourceBasedCodeCoverage.rst | 45 ++++++++ clang/docs/UsersManual.rst | 13 +++ 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 +++++ 29 files changed, 403 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 fc42bcf..0ec9931 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/SourceBasedCodeCoverage.rst b/clang/docs/SourceBasedCodeCoverage.rst index 90feb41..587e47c 100644 --- a/clang/docs/SourceBasedCodeCoverage.rst +++ b/clang/docs/SourceBasedCodeCoverage.rst @@ -64,6 +64,51 @@ To compile code with coverage enabled, pass ``-fprofile-instr-generate Note that linking together code with and without coverage instrumentation is supported. Uninstrumented code simply won't be accounted for in reports. +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=file.list`` option. 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 + +The file uses :doc:`SanitizerSpecialCaseList` format. 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. For example: + +.. 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. + Running the instrumented program ================================ diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index fe944a0..3827614 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2268,6 +2268,17 @@ programs using the same instrumentation method as ``-fprofile-generate``. This option currently works with ``-fprofile-arcs`` and ``-fprofile-instr-generate``, but not with ``-fprofile-generate``. +.. option:: -fprofile-list= + + This option can be used to apply profile instrumentation only to selected + files or functions. ``pathname`` specifies a file in the sanitizer special + case list 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 + Disabling Instrumentation ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -3740,6 +3751,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 bc86022..ce47d54 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 e1d41c3..d8bd2a8 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 0000000..989c365 --- /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 7ef0c52..3213e35 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 44545f0..cb7f00a 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 b1c5003..e531948 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 0000000..56bc37a --- /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 2c302ae4..b393c88 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 9d11466..8eb7adb 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 d93c969..31afbc6 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 c595705..618e2f8 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 052d58a..08ae877 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 1b6973a..689afc6 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5515,6 +5515,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 f6e392f..b9e3d69 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1355,6 +1355,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 0000000..27a7174 --- /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 -e "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" | sed -e 's/\\/\\\\/g' > %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 -e "!fun:test1" | sed -e 's/\\/\\\\/g' > %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 03dac9e..5b4854d 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 4546074..f7ffc88 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 3c35c9b..427de74 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 eff81e36..2a3fb8f 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 32e1165..5149f86 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 4d886f7..37ecb99 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 b17673f..c4629de 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 2b12e65..100e881 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 17ccf3a..be6c8c6 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 2a7c62b..390925a 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 0000000..d7df07b --- /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 } -- 2.7.4