diff --git a/clang/docs/ClangCommandLineReference.rst b/clang/docs/ClangCommandLineReference.rst --- a/clang/docs/ClangCommandLineReference.rst +++ b/clang/docs/ClangCommandLineReference.rst @@ -2045,6 +2045,12 @@ 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 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2282,6 +2282,63 @@ 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 @@ 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 --- 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 @@ /// 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 @@ 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 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -285,6 +285,10 @@ /// 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 --- /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 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1151,6 +1151,10 @@ 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 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -965,6 +965,7 @@ 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 --- a/clang/lib/Basic/CMakeLists.txt +++ b/clang/lib/Basic/CMakeLists.txt @@ -59,6 +59,7 @@ 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 --- /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.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -1442,7 +1442,8 @@ /// 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/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -839,6 +839,10 @@ } } + 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/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1277,6 +1277,10 @@ 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/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2563,6 +2563,34 @@ 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/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp --- a/clang/lib/CodeGen/CodeGenPGO.cpp +++ b/clang/lib/CodeGen/CodeGenPGO.cpp @@ -811,6 +811,9 @@ 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 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -5531,6 +5531,14 @@ 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 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1358,6 +1358,10 @@ } } + // -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 --- /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 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -656,6 +656,7 @@ 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 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -148,6 +148,9 @@ /// 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 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -663,6 +663,7 @@ 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 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -1368,6 +1368,7 @@ 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 @@ 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 @@ 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 --- a/llvm/lib/AsmParser/LLToken.h +++ b/llvm/lib/AsmParser/LLToken.h @@ -210,6 +210,7 @@ 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 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -680,6 +680,8 @@ 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 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -403,6 +403,8 @@ 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 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1655,6 +1655,7 @@ 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 --- a/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp +++ b/llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp @@ -1591,6 +1591,8 @@ 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 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -973,6 +973,7 @@ 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 --- /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 }