Index: clang/docs/SourceBasedCodeCoverage.rst =================================================================== --- clang/docs/SourceBasedCodeCoverage.rst +++ clang/docs/SourceBasedCodeCoverage.rst @@ -64,6 +64,46 @@ 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. When set, only +the files and functions specified in ``file.list`` will be instrumented. The +option can be specified multiple times to pass multiple files: + +.. code-block:: console + + % clang++ -fprofile-instr-generate -fcoverage-mapping -fprofile-list=file1.list -fprofile-list=file2.list foo.cc -o foo + +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 + Running the instrumented program ================================ Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ 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 { Index: clang/include/clang/Basic/LangOptions.h =================================================================== --- clang/include/clang/Basic/LangOptions.h +++ clang/include/clang/Basic/LangOptions.h @@ -281,6 +281,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; Index: clang/include/clang/Basic/ProfileList.h =================================================================== --- /dev/null +++ clang/include/clang/Basic/ProfileList.h @@ -0,0 +1,52 @@ +//===--- 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/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 { + const bool Empty; + std::unique_ptr SCL; + SourceManager &SM; + +public: + ProfileList(ArrayRef Paths, SourceManager &SM); + ~ProfileList(); + + bool isEmpty() const { return Empty; } + + llvm::Optional shouldInstrumentFunction(StringRef FunctionName, + StringRef Section) const; + llvm::Optional shouldInstrumentLocation(SourceLocation Loc, + StringRef Section) const; + llvm::Optional shouldInstrumentFile(StringRef FileName, + StringRef Section) const; +}; + +} // namespace clang + +#endif Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -1176,6 +1176,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<"LangOpts->ProfileListFiles">; defm addrsig : BoolFOption<"addrsig", "CodeGenOpts.Addrsig", DefaultsToFalse, Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ 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), Index: clang/lib/Basic/CMakeLists.txt =================================================================== --- clang/lib/Basic/CMakeLists.txt +++ clang/lib/Basic/CMakeLists.txt @@ -58,6 +58,7 @@ ObjCRuntime.cpp OpenMPKinds.cpp OperatorPrecedence.cpp + ProfileList.cpp SanitizerBlacklist.cpp SanitizerSpecialCaseList.cpp Sanitizers.cpp Index: clang/lib/Basic/ProfileList.cpp =================================================================== --- /dev/null +++ clang/lib/Basic/ProfileList.cpp @@ -0,0 +1,54 @@ +//===--- 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; + +ProfileList::ProfileList(ArrayRef Paths, SourceManager &SM) + : Empty(Paths.empty()), + SCL(llvm::SpecialCaseList::createOrDie( + Paths, SM.getFileManager().getVirtualFileSystem())), + SM(SM) {} + +ProfileList::~ProfileList() = default; + +llvm::Optional +ProfileList::shouldInstrumentFunction(StringRef FunctionName, + StringRef Section) const { + if (SCL->inSection(Section, "!fun", FunctionName)) + return false; + if (SCL->inSection(Section, "fun", FunctionName)) + return true; + return None; +} + +llvm::Optional +ProfileList::shouldInstrumentLocation(SourceLocation Loc, + StringRef Section) const { + return shouldInstrumentFile(SM.getFilename(SM.getFileLoc(Loc)), Section); +} + +llvm::Optional +ProfileList::shouldInstrumentFile(StringRef FileName, StringRef Section) const { + if (SCL->inSection(Section, "!src", FileName)) + return false; + if (SCL->inSection(Section, "src", FileName)) + return true; + return None; +} Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ 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); } Index: clang/lib/CodeGen/CodeGenFunction.cpp =================================================================== --- clang/lib/CodeGen/CodeGenFunction.cpp +++ clang/lib/CodeGen/CodeGenFunction.cpp @@ -839,6 +839,23 @@ } } + if (CGM.getCodeGenOpts().getProfileInstr() != CodeGenOptions::ProfileNone) { + auto SectionName = [&]() { + switch (CGM.getCodeGenOpts().getProfileInstr()) { + case CodeGenOptions::ProfileClangInstr: + return "clang"; + case CodeGenOptions::ProfileIRInstr: + return "llvm"; + case CodeGenOptions::ProfileCSIRInstr: + return "csllvm"; + default: + llvm_unreachable("unknown instrumentation type"); + } + }; + if (!CGM.isProfileInstrumented(Fn, Loc, SectionName())) + Fn->addFnAttr(llvm::Attribute::NoProfile); + } + unsigned Count, Offset; if (const auto *Attr = D ? D->getAttr() : nullptr) { Index: clang/lib/CodeGen/CodeGenModule.h =================================================================== --- clang/lib/CodeGen/CodeGenModule.h +++ clang/lib/CodeGen/CodeGenModule.h @@ -1277,6 +1277,12 @@ bool imbueXRayAttrs(llvm::Function *Fn, SourceLocation Loc, StringRef Category = StringRef()) const; + /// Returns true if profile instrumentation should be applied to the function + /// at the given location. The optional section name parameter can be used to + /// to also consider specific instrumentations. + bool isProfileInstrumented(llvm::Function *Fn, SourceLocation Loc, + StringRef Section = StringRef()) const; + SanitizerMetadata *getSanitizerMetadata() { return SanitizerMD.get(); } Index: clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- clang/lib/CodeGen/CodeGenModule.cpp +++ clang/lib/CodeGen/CodeGenModule.cpp @@ -2561,6 +2561,38 @@ return true; } +bool CodeGenModule::isProfileInstrumented(llvm::Function *Fn, + SourceLocation Loc, + StringRef Section) const { + if (getCodeGenOpts().hasProfileClangInstr()) + return false; + const auto &ProfileList = getContext().getProfileList(); + // If the profile list is empty, then instrument everything. + if (ProfileList.isEmpty()) + return true; + // Check function first. + Optional V = + ProfileList.shouldInstrumentFunction(Fn->getName(), Section); + if (V.hasValue()) + return *V; + // Check location next. + if (Loc.isValid()) { + Optional V = ProfileList.shouldInstrumentLocation(Loc, Section); + 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.shouldInstrumentFile(MainFile->getName(), Section); + if (V.hasValue()) + return *V; + } + return false; +} + bool CodeGenModule::MustBeEmitted(const ValueDecl *Global) { // Never defer when EmitAllDecls is specified. if (LangOpts.EmitAllDecls) Index: clang/lib/CodeGen/CodeGenPGO.cpp =================================================================== --- clang/lib/CodeGen/CodeGenPGO.cpp +++ 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); Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -5495,6 +5495,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; Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -1341,6 +1341,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()); Index: clang/test/CodeGen/profile-filter.c =================================================================== --- /dev/null +++ clang/test/CodeGen/profile-filter.c @@ -0,0 +1,53 @@ +// 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 + +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; +} Index: llvm/include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ 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 { Index: llvm/include/llvm/IR/Attributes.td =================================================================== --- llvm/include/llvm/IR/Attributes.td +++ 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">; Index: llvm/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/lib/AsmParser/LLLexer.cpp +++ llvm/lib/AsmParser/LLLexer.cpp @@ -663,6 +663,7 @@ KEYWORD(nonlazybind); KEYWORD(nomerge); KEYWORD(nonnull); + KEYWORD(noprofile); KEYWORD(noredzone); KEYWORD(noreturn); KEYWORD(nosync); Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ 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: Index: llvm/lib/AsmParser/LLToken.h =================================================================== --- llvm/lib/AsmParser/LLToken.h +++ llvm/lib/AsmParser/LLToken.h @@ -210,6 +210,7 @@ kw_nonlazybind, kw_nomerge, kw_nonnull, + kw_noprofile, kw_noredzone, kw_noreturn, kw_nosync, Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ 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: Index: llvm/lib/IR/Attributes.cpp =================================================================== --- llvm/lib/IR/Attributes.cpp +++ 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)) Index: llvm/lib/IR/Verifier.cpp =================================================================== --- llvm/lib/IR/Verifier.cpp +++ llvm/lib/IR/Verifier.cpp @@ -1639,6 +1639,7 @@ case Attribute::StrictFP: case Attribute::NullPointerIsValid: case Attribute::MustProgress: + case Attribute::NoProfile: return true; default: break; Index: llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp =================================================================== --- llvm/lib/Transforms/Instrumentation/PGOInstrumentation.cpp +++ 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); Index: llvm/lib/Transforms/Utils/CodeExtractor.cpp =================================================================== --- llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -974,6 +974,7 @@ case Attribute::UWTable: case Attribute::NoCfCheck: case Attribute::MustProgress: + case Attribute::NoProfile: break; } Index: llvm/test/Transforms/PGOProfile/noprofile.ll =================================================================== --- /dev/null +++ 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 }