From b692312ca432d9a379f67a8d83177a6f1722baaa Mon Sep 17 00:00:00 2001 From: Ellis Hoag Date: Fri, 29 Jul 2022 14:49:44 -0700 Subject: [PATCH] [InstrProf] Add new format for -fprofile-list= In D130807 we added the `skipprofile` attribute. This commit changes the format so we can either `forbid` or `skip` profiling functions by adding the `noprofile` or `skipprofile` attributes, respectively. The behavior of the original format remains unchanged. Also, add the `skipprofile` attribute when using `-fprofile-function-groups`. Reviewed By: phosek Differential Revision: https://reviews.llvm.org/D130808 --- clang/docs/UsersManual.rst | 75 ++++++++++++++++++---------- clang/include/clang/Basic/ProfileList.h | 23 +++++++-- clang/lib/Basic/ProfileList.cpp | 53 ++++++++++++++++---- clang/lib/CodeGen/CodeGenFunction.cpp | 13 ++++- clang/lib/CodeGen/CodeGenModule.cpp | 38 +++++++------- clang/lib/CodeGen/CodeGenModule.h | 9 ++-- clang/test/CodeGen/profile-filter-new.c | 27 ++++++++++ clang/test/CodeGen/profile-function-groups.c | 12 ++--- 8 files changed, 178 insertions(+), 72 deletions(-) create mode 100644 clang/test/CodeGen/profile-filter-new.c diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 15df488..f9ccca6 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2500,43 +2500,66 @@ This can be done using the ``-fprofile-list`` option. .. 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. + The option can be specified multiple times to pass multiple files. -.. code-block:: console + .. code-block:: console + + $ clang++ -O2 -fprofile-instr-generate -fcoverage-mapping -fprofile-list=fun.list -fprofile-list=code.list code.cc -o code + +Supported sections are ``[clang]``, ``[llvm]``, and ``[csllvm]`` representing +clang PGO, IRPGO, and CSIRPGO, respectively. Supported prefixes are ``function`` +and ``source``. Supported categories are ``allow``, ``skip``, and ``forbid``. +``skip`` adds the ``skipprofile`` attribute while ``forbid`` adds the +``noprofile`` attribute to the appropriate function. Use +``default:`` to specify the default category. + + .. code-block:: console + + $ cat fun.list + # The following cases are for clang instrumentation. + [clang] + + # We might not want to profile functions that are inlined in many places. + function:inlinedLots=skip + + # We want to forbid profiling where it might be dangerous. + source:lib/unsafe/*.cc=forbid - $ 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 + # Otherwise we allow profiling. + default:allow -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. +Older Prefixes +"""""""""""""" + An older format is also supported, but it is only able to add the + ``noprofile`` attribute. + To filter individual functions or entire source files use ``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 + .. code-block:: none - # all functions whose name starts with foo will be instrumented. - fun:foo* + # all functions whose name starts with foo will be instrumented. + fun:foo* - # except for foo1 which will be excluded from instrumentation. - !fun:foo1 + # 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 + # 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 + # 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. + 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. Instrument function groups ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/ProfileList.h b/clang/include/clang/Basic/ProfileList.h index aa472f1..f776800 100644 --- a/clang/include/clang/Basic/ProfileList.h +++ b/clang/include/clang/Basic/ProfileList.h @@ -26,25 +26,38 @@ namespace clang { class ProfileSpecialCaseList; class ProfileList { +public: + /// Represents if an how something should be excluded from profiling. + enum ExclusionType { + /// Profiling is allowed. + Allow, + /// Profiling is skipped using the \p skipprofile attribute. + Skip, + /// Profiling is forbidden using the \p noprofile attribute. + Forbid, + }; + +private: std::unique_ptr SCL; const bool Empty; - const bool Default; SourceManager &SM; + llvm::Optional inSection(StringRef Section, StringRef Prefix, + StringRef Query) const; public: ProfileList(ArrayRef Paths, SourceManager &SM); ~ProfileList(); bool isEmpty() const { return Empty; } - bool getDefault() const { return Default; } + ExclusionType getDefault(CodeGenOptions::ProfileInstrKind Kind) const; - llvm::Optional + llvm::Optional isFunctionExcluded(StringRef FunctionName, CodeGenOptions::ProfileInstrKind Kind) const; - llvm::Optional + llvm::Optional isLocationExcluded(SourceLocation Loc, CodeGenOptions::ProfileInstrKind Kind) const; - llvm::Optional + llvm::Optional isFileExcluded(StringRef FileName, CodeGenOptions::ProfileInstrKind Kind) const; }; diff --git a/clang/lib/Basic/ProfileList.cpp b/clang/lib/Basic/ProfileList.cpp index 9c88559..4c17a54 100644 --- a/clang/lib/Basic/ProfileList.cpp +++ b/clang/lib/Basic/ProfileList.cpp @@ -66,8 +66,7 @@ ProfileSpecialCaseList::createOrDie(const std::vector &Paths, 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) {} + Empty(SCL->isEmpty()), SM(SM) {} ProfileList::~ProfileList() = default; @@ -85,30 +84,66 @@ static StringRef getSectionName(CodeGenOptions::ProfileInstrKind Kind) { llvm_unreachable("Unhandled CodeGenOptions::ProfileInstrKind enum"); } -llvm::Optional +ProfileList::ExclusionType +ProfileList::getDefault(CodeGenOptions::ProfileInstrKind Kind) const { + StringRef Section = getSectionName(Kind); + // Check for "default:" + if (SCL->inSection(Section, "default", "allow")) + return Allow; + if (SCL->inSection(Section, "default", "skip")) + return Skip; + if (SCL->inSection(Section, "default", "forbid")) + return Forbid; + // If any cases use "fun" or "src", set the default to FORBID. + if (SCL->hasPrefix("fun") || SCL->hasPrefix("src")) + return Forbid; + return Allow; +} + +llvm::Optional +ProfileList::inSection(StringRef Section, StringRef Prefix, + StringRef Query) const { + if (SCL->inSection(Section, Prefix, Query, "allow")) + return Allow; + if (SCL->inSection(Section, Prefix, Query, "skip")) + return Skip; + if (SCL->inSection(Section, Prefix, Query, "forbid")) + return Forbid; + if (SCL->inSection(Section, Prefix, Query)) + return Allow; + return None; +} + +llvm::Optional ProfileList::isFunctionExcluded(StringRef FunctionName, CodeGenOptions::ProfileInstrKind Kind) const { StringRef Section = getSectionName(Kind); + // Check for "function:=" + if (auto V = inSection(Section, "function", FunctionName)) + return V; if (SCL->inSection(Section, "!fun", FunctionName)) - return true; + return Forbid; if (SCL->inSection(Section, "fun", FunctionName)) - return false; + return Allow; return None; } -llvm::Optional +llvm::Optional ProfileList::isLocationExcluded(SourceLocation Loc, CodeGenOptions::ProfileInstrKind Kind) const { return isFileExcluded(SM.getFilename(SM.getFileLoc(Loc)), Kind); } -llvm::Optional +llvm::Optional ProfileList::isFileExcluded(StringRef FileName, CodeGenOptions::ProfileInstrKind Kind) const { StringRef Section = getSectionName(Kind); + // Check for "source:=" + if (auto V = inSection(Section, "source", FileName)) + return V; if (SCL->inSection(Section, "!src", FileName)) - return true; + return Forbid; if (SCL->inSection(Section, "src", FileName)) - return false; + return Allow; return None; } diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index d2f2515..7997a07 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -851,9 +851,18 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, } } - if (CGM.getCodeGenOpts().getProfileInstr() != CodeGenOptions::ProfileNone) - if (CGM.isFunctionBlockedFromProfileInstr(Fn, Loc)) + if (CGM.getCodeGenOpts().getProfileInstr() != CodeGenOptions::ProfileNone) { + switch (CGM.isFunctionBlockedFromProfileInstr(Fn, Loc)) { + case ProfileList::Skip: + Fn->addFnAttr(llvm::Attribute::SkipProfile); + break; + case ProfileList::Forbid: Fn->addFnAttr(llvm::Attribute::NoProfile); + break; + case ProfileList::Allow: + break; + } + } unsigned Count, Offset; if (const auto *Attr = diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 537cb78..8771f9e 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2895,46 +2895,44 @@ bool CodeGenModule::imbueXRayAttrs(llvm::Function *Fn, SourceLocation Loc, return true; } -bool CodeGenModule::isFunctionBlockedByProfileList(llvm::Function *Fn, - SourceLocation Loc) const { +ProfileList::ExclusionType +CodeGenModule::isFunctionBlockedByProfileList(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; + return ProfileList::Allow; CodeGenOptions::ProfileInstrKind Kind = getCodeGenOpts().getProfileInstr(); // First, check the function name. - Optional V = ProfileList.isFunctionExcluded(Fn->getName(), Kind); - if (V) + if (auto V = ProfileList.isFunctionExcluded(Fn->getName(), Kind)) return *V; // Next, check the source location. - if (Loc.isValid()) { - Optional V = ProfileList.isLocationExcluded(Loc, Kind); - if (V) + if (Loc.isValid()) + if (auto V = ProfileList.isLocationExcluded(Loc, Kind)) 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) + if (const auto *MainFile = SM.getFileEntryForID(SM.getMainFileID())) + if (auto V = ProfileList.isFileExcluded(MainFile->getName(), Kind)) return *V; - } - return ProfileList.getDefault(); + return ProfileList.getDefault(Kind); } -bool CodeGenModule::isFunctionBlockedFromProfileInstr( - llvm::Function *Fn, SourceLocation Loc) const { - if (isFunctionBlockedByProfileList(Fn, Loc)) - return true; +ProfileList::ExclusionType +CodeGenModule::isFunctionBlockedFromProfileInstr(llvm::Function *Fn, + SourceLocation Loc) const { + auto V = isFunctionBlockedByProfileList(Fn, Loc); + if (V != ProfileList::Allow) + return V; auto NumGroups = getCodeGenOpts().ProfileTotalFunctionGroups; if (NumGroups > 1) { auto Group = llvm::crc32(arrayRefFromStringRef(Fn->getName())) % NumGroups; if (Group != getCodeGenOpts().ProfileSelectedFunctionGroup) - return true; + return ProfileList::Skip; } - return false; + return ProfileList::Allow; } bool CodeGenModule::MustBeEmitted(const ValueDecl *Global) { diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 5fbcc5a..14c791f 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1351,13 +1351,14 @@ public: /// \returns true if \p Fn at \p Loc should be excluded from profile /// instrumentation by the SCL passed by \p -fprofile-list. - bool isFunctionBlockedByProfileList(llvm::Function *Fn, - SourceLocation Loc) const; + ProfileList::ExclusionType + isFunctionBlockedByProfileList(llvm::Function *Fn, SourceLocation Loc) const; /// \returns true if \p Fn at \p Loc should be excluded from profile /// instrumentation. - bool isFunctionBlockedFromProfileInstr(llvm::Function *Fn, - SourceLocation Loc) const; + ProfileList::ExclusionType + isFunctionBlockedFromProfileInstr(llvm::Function *Fn, + SourceLocation Loc) const; SanitizerMetadata *getSanitizerMetadata() { return SanitizerMD.get(); diff --git a/clang/test/CodeGen/profile-filter-new.c b/clang/test/CodeGen/profile-filter-new.c new file mode 100644 index 0000000..c6fdc31 --- /dev/null +++ b/clang/test/CodeGen/profile-filter-new.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fprofile-instrument=llvm -emit-llvm %s -o - | FileCheck %s --implicit-check-not="; {{.* (noprofile|skipprofile)}}" + +// RUN: echo -e "[llvm]\nfunction:foo=skip" > %t0.list +// RUN: %clang_cc1 -fprofile-instrument=llvm -fprofile-list=%t0.list -emit-llvm %s -o - | FileCheck %s --implicit-check-not="; {{.* (noprofile|skipprofile)}}" --check-prefixes=CHECK,SKIP-FOO + +// RUN: echo -e "[csllvm]\nfunction:bar=forbid" > %t1.list +// RUN: %clang_cc1 -fprofile-instrument=csllvm -fprofile-list=%t1.list -emit-llvm %s -o - | FileCheck %s --implicit-check-not="; {{.* (noprofile|skipprofile)}}" --check-prefixes=CHECK,FORBID-BAR +// RUN: %clang_cc1 -fprofile-instrument=llvm -fprofile-list=%t1.list -emit-llvm %s -o - | FileCheck %s --implicit-check-not="; {{.* (noprofile|skipprofile)}}" + +// RUN: echo -e "[llvm]\ndefault:forbid\nfunction:foo=allow" > %t2.list +// RUN: %clang_cc1 -fprofile-instrument=llvm -fprofile-list=%t2.list -emit-llvm %s -o - | FileCheck %s --implicit-check-not="; {{.* (noprofile|skipprofile)}}" --check-prefixes=CHECK,FORBID + +// RUN: echo -e "[llvm]\nsource:%s=forbid\nfunction:foo=allow" | sed -e 's/\\/\\\\/g' > %t2.list +// RUN: %clang_cc1 -fprofile-instrument=llvm -fprofile-list=%t2.list -emit-llvm %s -o - | FileCheck %s --implicit-check-not="; {{.* (noprofile|skipprofile)}}" --check-prefixes=CHECK,FORBID + +// SKIP-FOO: skipprofile +// CHECK-LABEL: define {{.*}} @foo +int foo(int a) { return 4 * a + 1; } + +// FORBID-BAR: noprofile +// FORBID: noprofile +// CHECK-LABEL: define {{.*}} @bar +int bar(int a) { return 4 * a + 2; } + +// FORBID: noprofile +// CHECK-LABEL: define {{.*}} @goo +int goo(int a) { return 4 * a + 3; } diff --git a/clang/test/CodeGen/profile-function-groups.c b/clang/test/CodeGen/profile-function-groups.c index 232abd7..052e867 100644 --- a/clang/test/CodeGen/profile-function-groups.c +++ b/clang/test/CodeGen/profile-function-groups.c @@ -4,21 +4,21 @@ // Group 0 -// SELECT1: noprofile -// SELECT2: noprofile +// SELECT1: skipprofile +// SELECT2: skipprofile // CHECK: define {{.*}} @hoo() void hoo() {} // Group 1 -// SELECT0: noprofile +// SELECT0: skipprofile -// SELECT2: noprofile +// SELECT2: skipprofile // CHECK: define {{.*}} @goo() void goo() {} // Group 2 -// SELECT0: noprofile -// SELECT1: noprofile +// SELECT0: skipprofile +// SELECT1: skipprofile // CHECK: define {{.*}} @boo() void boo() {} -- 2.7.4