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
This commit is contained in:
Petr Hosek 2021-01-15 01:14:37 -08:00
parent 32cc5564e2
commit 4edf35f11a
29 changed files with 403 additions and 1 deletions

View File

@ -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 <pathname>/default.profdata. Otherwise, it reads from file <pathname>.
.. program:: clang1
.. option:: -fprofile-list=<file>
.. 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

View File

@ -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:<name>`` or ``src:<file>``
respectively. To exclude a function or a source file, use ``!fun:<name>`` or
``!src:<file>`` 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
================================

View File

@ -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=<pathname>
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=<file>
Use the remappings described in <file> to match the profile data against names in the program
-fprofile-list=<file>
Filename defining the list of functions/files to instrument
-fsanitize-address-field-padding=<value>
Level of field padding for AddressSanitizer
-fsanitize-address-globals-dead-stripping

View File

@ -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<XRayFunctionFilter> XRayFilter;
/// ProfileList object that is used by the profile instrumentation
/// to decide which entities should be instrumented.
std::unique_ptr<ProfileList> 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 {

View File

@ -285,6 +285,10 @@ public:
/// attribute(s).
std::vector<std::string> XRayAttrListFiles;
/// Paths to special case list files specifying which entities
/// (files, functions) should or should not be instrumented.
std::vector<std::string> ProfileListFiles;
clang::ObjCRuntime ObjCRuntime;
CoreFoundationABI CFRuntime = CoreFoundationABI::Unspecified;

View File

@ -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 <memory>
namespace llvm {
class SpecialCaseList;
}
namespace clang {
class ProfileSpecialCaseList;
class ProfileList {
std::unique_ptr<ProfileSpecialCaseList> SCL;
const bool Empty;
const bool Default;
SourceManager &SM;
public:
ProfileList(ArrayRef<std::string> Paths, SourceManager &SM);
~ProfileList();
bool isEmpty() const { return Empty; }
bool getDefault() const { return Default; }
llvm::Optional<bool>
isFunctionExcluded(StringRef FunctionName,
CodeGenOptions::ProfileInstrKind Kind) const;
llvm::Optional<bool>
isLocationExcluded(SourceLocation Loc,
CodeGenOptions::ProfileInstrKind Kind) const;
llvm::Optional<bool>
isFileExcluded(StringRef FileName,
CodeGenOptions::ProfileInstrKind Kind) const;
};
} // namespace clang
#endif

View File

@ -1151,6 +1151,10 @@ defm pseudo_probe_for_profiling : BoolFOption<"pseudo-probe-for-profiling",
def forder_file_instrumentation : Flag<["-"], "forder-file-instrumentation">,
Group<f_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<f_Group>, Flags<[CC1Option, CoreOption]>,
HelpText<"Filename defining the list of functions/files to instrument">,
MarshallingInfoStringVector<LangOpts<"ProfileListFiles">>;
defm addrsig : BoolFOption<"addrsig",
CodeGenOpts<"Addrsig">, DefaultFalse,

View File

@ -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),

View File

@ -59,6 +59,7 @@ add_clang_library(clangBasic
OpenCLOptions.cpp
OpenMPKinds.cpp
OperatorPrecedence.cpp
ProfileList.cpp
SanitizerBlacklist.cpp
SanitizerSpecialCaseList.cpp
Sanitizers.cpp

View File

@ -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<ProfileSpecialCaseList>
create(const std::vector<std::string> &Paths, llvm::vfs::FileSystem &VFS,
std::string &Error);
static std::unique_ptr<ProfileSpecialCaseList>
createOrDie(const std::vector<std::string> &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>
ProfileSpecialCaseList::create(const std::vector<std::string> &Paths,
llvm::vfs::FileSystem &VFS,
std::string &Error) {
auto PSCL = std::make_unique<ProfileSpecialCaseList>();
if (PSCL->createInternal(Paths, VFS, Error))
return PSCL;
return nullptr;
}
std::unique_ptr<ProfileSpecialCaseList>
ProfileSpecialCaseList::createOrDie(const std::vector<std::string> &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<std::string> 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<bool>
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<bool>
ProfileList::isLocationExcluded(SourceLocation Loc,
CodeGenOptions::ProfileInstrKind Kind) const {
return isFileExcluded(SM.getFilename(SM.getFileLoc(Loc)), Kind);
}
llvm::Optional<bool>
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;
}

View File

@ -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<PatchableFunctionEntryAttr>() : nullptr) {

View File

@ -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);
}

View File

@ -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<bool> V = ProfileList.isFunctionExcluded(Fn->getName(), Kind);
if (V.hasValue())
return *V;
// Next, check the source location.
if (Loc.isValid()) {
Optional<bool> 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<bool> 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)

View File

@ -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();
}

View File

@ -811,6 +811,9 @@ void CodeGenPGO::assignRegionCounters(GlobalDecl GD, llvm::Function *Fn) {
if (isa<CXXDestructorDecl>(D) && GD.getDtorType() != Dtor_Base)
return;
if (Fn->hasFnAttribute(llvm::Attribute::NoProfile))
return;
CGM.ClearUnusedCoverageMapping(D);
setFuncName(Fn);

View File

@ -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;

View File

@ -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());

View File

@ -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;
}

View File

@ -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 {

View File

@ -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">;

View File

@ -663,6 +663,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(nonlazybind);
KEYWORD(nomerge);
KEYWORD(nonnull);
KEYWORD(noprofile);
KEYWORD(noredzone);
KEYWORD(noreturn);
KEYWORD(nosync);

View File

@ -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:

View File

@ -210,6 +210,7 @@ enum Kind {
kw_nonlazybind,
kw_nomerge,
kw_nonnull,
kw_noprofile,
kw_noredzone,
kw_noreturn,
kw_nosync,

View File

@ -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:

View File

@ -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))

View File

@ -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;

View File

@ -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);

View File

@ -973,6 +973,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
case Attribute::UWTable:
case Attribute::NoCfCheck:
case Attribute::MustProgress:
case Attribute::NoProfile:
break;
}

View File

@ -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 }