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 @@ -1522,7 +1522,8 @@ /// If \p StepV is null, the default increment is 1. void incrementProfileCounter(const Stmt *S, llvm::Value *StepV = nullptr) { if (CGM.getCodeGenOpts().hasProfileClangInstr() && - !CurFn->hasFnAttribute(llvm::Attribute::NoProfile)) + !CurFn->hasFnAttribute(llvm::Attribute::NoProfile) && + !CurFn->hasFnAttribute(llvm::Attribute::SkipProfile)) PGO.emitCounterIncrement(Builder, S, StepV); PGO.setCurrentStmt(S); } 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 @@ -822,6 +822,8 @@ CGM.ClearUnusedCoverageMapping(D); if (Fn->hasFnAttribute(llvm::Attribute::NoProfile)) return; + if (Fn->hasFnAttribute(llvm::Attribute::SkipProfile)) + return; setFuncName(Fn); diff --git a/clang/test/CodeGen/profile-function-groups.c b/clang/test/CodeGen/profile-function-groups.c --- a/clang/test/CodeGen/profile-function-groups.c +++ b/clang/test/CodeGen/profile-function-groups.c @@ -1,9 +1,9 @@ -// RUN: %clang -fprofile-generate -fprofile-function-groups=3 -fprofile-selected-function-group=0 -emit-llvm -S %s -o - | FileCheck %s --check-prefixes=CHECK,SELECT0 -// RUN: %clang -fprofile-generate -fprofile-function-groups=3 -fprofile-selected-function-group=1 -emit-llvm -S %s -o - | FileCheck %s --check-prefixes=CHECK,SELECT1 -// RUN: %clang -fprofile-generate -fprofile-function-groups=3 -fprofile-selected-function-group=2 -emit-llvm -S %s -o - | FileCheck %s --check-prefixes=CHECK,SELECT2 +// RUN: %clang -fprofile-generate -fprofile-function-groups=3 -fprofile-selected-function-group=0 -emit-llvm -S %s -o - | FileCheck %s --implicit-check-not="; {{.* (noprofile|skipprofile)}}" --check-prefixes=CHECK,SELECT0 +// RUN: %clang -fprofile-generate -fprofile-function-groups=3 -fprofile-selected-function-group=1 -emit-llvm -S %s -o - | FileCheck %s --implicit-check-not="; {{.* (noprofile|skipprofile)}}" --check-prefixes=CHECK,SELECT1 +// RUN: %clang -fprofile-generate -fprofile-function-groups=3 -fprofile-selected-function-group=2 -emit-llvm -S %s -o - | FileCheck %s --implicit-check-not="; {{.* (noprofile|skipprofile)}}" --check-prefixes=CHECK,SELECT2 // Group 0 -// SELECT0-NOT: noprofile + // SELECT1: noprofile // SELECT2: noprofile // CHECK: define {{.*}} @hoo() @@ -11,7 +11,7 @@ // Group 1 // SELECT0: noprofile -// SELECT1-NOT: noprofile + // SELECT2: noprofile // CHECK: define {{.*}} @goo() void goo() {} @@ -19,6 +19,6 @@ // Group 2 // SELECT0: noprofile // SELECT1: noprofile -// SELECT2-NOT: noprofile + // CHECK: define {{.*}} @boo() void boo() {} diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1803,8 +1803,14 @@ startup time if the function is not called during program startup. ``noprofile`` This function attribute prevents instrumentation based profiling, used for - coverage or profile based optimization, from being added to a function, - even when inlined. + coverage or profile based optimization, from being added to a function. It + also blocks inlining if the caller and callee have different values of this + attribute. +``skipprofile`` + This function attribute prevents instrumentation based profiling, used for + coverage or profile based optimization, from being added to a function. This + attribute does not restrict inlining, so instrumented instruction could end + up in this function. ``noredzone`` This attribute indicates that the code generator should not use a red zone, even if the target-specific ABI normally permits it. 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 @@ -689,6 +689,7 @@ ATTR_KIND_ALLOC_KIND = 82, ATTR_KIND_PRESPLIT_COROUTINE = 83, ATTR_KIND_FNRETTHUNK_EXTERN = 84, + ATTR_KIND_SKIP_PROFILE = 85, }; 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 @@ -186,6 +186,10 @@ /// Function should not be instrumented. def NoProfile : EnumAttr<"noprofile", [FnAttr]>; +/// This function should not be instrumented but it is ok to inline profiled +// functions into it. +def SkipProfile : EnumAttr<"skipprofile", [FnAttr]>; + /// Function doesn't unwind stack. def NoUnwind : EnumAttr<"nounwind", [FnAttr]>; diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1919,6 +1919,8 @@ return Attribute::NoCfCheck; case bitc::ATTR_KIND_NO_PROFILE: return Attribute::NoProfile; + case bitc::ATTR_KIND_SKIP_PROFILE: + return Attribute::SkipProfile; case bitc::ATTR_KIND_NO_UNWIND: return Attribute::NoUnwind; case bitc::ATTR_KIND_NO_SANITIZE_BOUNDS: 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 @@ -698,6 +698,8 @@ return bitc::ATTR_KIND_NOCF_CHECK; case Attribute::NoProfile: return bitc::ATTR_KIND_NO_PROFILE; + case Attribute::SkipProfile: + return bitc::ATTR_KIND_SKIP_PROFILE; case Attribute::NoUnwind: return bitc::ATTR_KIND_NO_UNWIND; case Attribute::NoSanitizeBounds: diff --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp --- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp +++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp @@ -797,6 +797,8 @@ if (isUsingScopeBasedEH(F)) continue; if (F.hasFnAttribute(llvm::Attribute::NoProfile)) continue; + if (F.hasFnAttribute(llvm::Attribute::SkipProfile)) + continue; // Add the function line number to the lines of the entry block // to have a counter for the function definition. 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 @@ -1574,6 +1574,8 @@ continue; if (F.hasFnAttribute(llvm::Attribute::NoProfile)) continue; + if (F.hasFnAttribute(llvm::Attribute::SkipProfile)) + 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 @@ -963,6 +963,7 @@ case Attribute::NoCfCheck: case Attribute::MustProgress: case Attribute::NoProfile: + case Attribute::SkipProfile: break; // These attributes cannot be applied to functions. case Attribute::Alignment: diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -535,6 +535,9 @@ ; CHECK: define void @f87() [[FNRETTHUNKEXTERN:#[0-9]+]] define void @f87() fn_ret_thunk_extern { ret void } +; CHECK: define void @f88() [[SKIPPROFILE:#[0-9]+]] +define void @f88() skipprofile { ret void } + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { readnone } @@ -589,4 +592,5 @@ ; CHECK: attributes #51 = { uwtable(sync) } ; CHECK: attributes #52 = { nosanitize_bounds } ; CHECK: attributes [[FNRETTHUNKEXTERN]] = { fn_ret_thunk_extern } +; CHECK: attributes [[SKIPPROFILE]] = { skipprofile } ; CHECK: attributes #[[NOBUILTIN]] = { nobuiltin } diff --git a/llvm/test/Transforms/GCOVProfiling/noprofile.ll b/llvm/test/Transforms/GCOVProfiling/noprofile.ll --- a/llvm/test/Transforms/GCOVProfiling/noprofile.ll +++ b/llvm/test/Transforms/GCOVProfiling/noprofile.ll @@ -10,6 +10,14 @@ ret i32 42, !dbg !27 } +; Test that the skipprofile attribute disables profiling. +define dso_local i32 @skip_instr(i32 %a) skipprofile { +; CHECK-LABEL: @skip_instr( +; CHECK-NEXT: ret i32 52 +; + ret i32 52 +} + define dso_local i32 @instr(i32 %a) !dbg !28 { ; CHECK-LABEL: @instr( ; CHECK-NEXT: [[GCOV_CTR:%.*]] = load i64, ptr @__llvm_gcov_ctr, align 4, !dbg [[DBG8:![0-9]+]] diff --git a/llvm/test/Transforms/PGOProfile/noprofile.ll b/llvm/test/Transforms/PGOProfile/noprofile.ll --- a/llvm/test/Transforms/PGOProfile/noprofile.ll +++ b/llvm/test/Transforms/PGOProfile/noprofile.ll @@ -21,4 +21,10 @@ ret i32 %sub } +define i32 @test3() skipprofile { +entry: +; CHECK-NOT: call void @llvm.instrprof.increment + ret i32 101 +} + attributes #0 = { noprofile }