diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -365,6 +365,9 @@ /// Whether emit extra debug info for sample pgo profile collection. CODEGENOPT(DebugInfoForProfiling, 1, 0) +/// Whether emit pseudo probes for sample pgo profile collection. +CODEGENOPT(PseudoProbeForProfiling, 1, 0) + /// Whether 3-component vector type is preserved. CODEGENOPT(PreserveVec3Type, 1, 0) 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 @@ -843,6 +843,12 @@ def fprofile_exclude_files_EQ : Joined<["-"], "fprofile-exclude-files=">, Group, Flags<[CC1Option, CoreOption]>, HelpText<"Instrument only functions from files where names don't match all the regexes separated by a semi-colon">; +def fpseudo_probe_for_profiling : Flag<["-"], "fpseudo-probe-for-profiling">, + Group, Flags<[DriverOption, CC1Option]>, + HelpText<"Emit pseudo probes for sample profiler">; +def fno_pseudo_probe_for_profiling : Flag<["-"], "fno-pseudo-probe-for-profiling">, + Group, Flags<[DriverOption, CC1Option]>, + HelpText<"Do not emit pseudo probes for sample profiler.">; 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)">; diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -1112,10 +1112,15 @@ CSAction, CodeGenOpts.DebugInfoForProfiling); } else if (!CodeGenOpts.SampleProfileFile.empty()) // -fprofile-sample-use + PGOOpt = PGOOptions( + CodeGenOpts.SampleProfileFile, "", CodeGenOpts.ProfileRemappingFile, + PGOOptions::SampleUse, PGOOptions::NoCSAction, + CodeGenOpts.DebugInfoForProfiling, CodeGenOpts.PseudoProbeForProfiling); + else if (CodeGenOpts.PseudoProbeForProfiling) + // -fpseudo-probe-for-profiling PGOOpt = - PGOOptions(CodeGenOpts.SampleProfileFile, "", - CodeGenOpts.ProfileRemappingFile, PGOOptions::SampleUse, - PGOOptions::NoCSAction, CodeGenOpts.DebugInfoForProfiling); + PGOOptions("", "", "", PGOOptions::NoAction, PGOOptions::NoCSAction, + CodeGenOpts.DebugInfoForProfiling, true); else if (CodeGenOpts.DebugInfoForProfiling) // -fdebug-info-for-profiling PGOOpt = PGOOptions("", "", "", PGOOptions::NoAction, 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 @@ -5443,6 +5443,10 @@ } Args.AddLastArg(CmdArgs, options::OPT_fprofile_remapping_file_EQ); + if (Args.hasFlag(options::OPT_fpseudo_probe_for_profiling, + options::OPT_fno_pseudo_probe_for_profiling, false)) + CmdArgs.push_back("-fpseudo-probe-for-profiling"); + RenderBuiltinOptions(TC, RawTriple, Args, CmdArgs); if (!Args.hasFlag(options::OPT_fassume_sane_operator_new, 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 @@ -872,6 +872,9 @@ std::string(Args.getLastArgValue(OPT_fprofile_sample_use_EQ)); Opts.DebugInfoForProfiling = Args.hasFlag( OPT_fdebug_info_for_profiling, OPT_fno_debug_info_for_profiling, false); + Opts.PseudoProbeForProfiling = + Args.hasFlag(OPT_fpseudo_probe_for_profiling, + OPT_fno_pseudo_probe_for_profiling, false); Opts.DebugNameTable = static_cast( Args.hasArg(OPT_ggnu_pubnames) ? llvm::DICompileUnit::DebugNameTableKind::GNU diff --git a/clang/test/CodeGen/emit-pseudo-probe.c b/clang/test/CodeGen/emit-pseudo-probe.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/emit-pseudo-probe.c @@ -0,0 +1,17 @@ +// RUN: %clang -O2 -fexperimental-new-pass-manager -fpseudo-probe-for-profiling -g -emit-llvm -S -o - %s | FileCheck %s + +// Check the generation of pseudoprobe intrinsic call + +void bar(); +void go(); + +void foo(int x) { + // CHECK: call void @llvm.pseudoprobe(i64 [[#GUID:]], i64 1) + if (x == 0) + // CHECK: call void @llvm.pseudoprobe(i64 [[#GUID]], i64 2) + bar(); + else + // CHECK: call void @llvm.pseudoprobe(i64 [[#GUID]], i64 3) + go(); + // CHECK: call void @llvm.pseudoprobe(i64 [[#GUID]], i64 4) +} diff --git a/llvm/include/llvm/Passes/PassBuilder.h b/llvm/include/llvm/Passes/PassBuilder.h --- a/llvm/include/llvm/Passes/PassBuilder.h +++ b/llvm/include/llvm/Passes/PassBuilder.h @@ -36,11 +36,15 @@ enum CSPGOAction { NoCSAction, CSIRInstr, CSIRUse }; PGOOptions(std::string ProfileFile = "", std::string CSProfileGenFile = "", std::string ProfileRemappingFile = "", PGOAction Action = NoAction, - CSPGOAction CSAction = NoCSAction, bool SamplePGOSupport = false) + CSPGOAction CSAction = NoCSAction, + bool DebugInfoForProfiling = false, + bool PseudoProbeForProfiling = false) : ProfileFile(ProfileFile), CSProfileGenFile(CSProfileGenFile), ProfileRemappingFile(ProfileRemappingFile), Action(Action), - CSAction(CSAction), - SamplePGOSupport(SamplePGOSupport || Action == SampleUse) { + CSAction(CSAction), DebugInfoForProfiling(DebugInfoForProfiling || + (Action == SampleUse && + !PseudoProbeForProfiling)), + PseudoProbeForProfiling(PseudoProbeForProfiling) { // Note, we do allow ProfileFile.empty() for Action=IRUse LTO can // callback with IRUse action without ProfileFile. @@ -55,16 +59,18 @@ // a profile. assert(this->CSAction != CSIRUse || this->Action == IRUse); - // If neither Action nor CSAction, SamplePGOSupport needs to be true. + // If neither Action nor CSAction, DebugInfoForProfiling or + // PseudoProbeForProfiling needs to be true. assert(this->Action != NoAction || this->CSAction != NoCSAction || - this->SamplePGOSupport); + this->DebugInfoForProfiling || this->PseudoProbeForProfiling); } std::string ProfileFile; std::string CSProfileGenFile; std::string ProfileRemappingFile; PGOAction Action; CSPGOAction CSAction; - bool SamplePGOSupport; + bool DebugInfoForProfiling; + bool PseudoProbeForProfiling; }; /// Tunable parameters for passes in the default pipelines. diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -901,6 +901,12 @@ OptimizationLevel Level, ThinLTOPhase Phase, bool DebugLogging) { ModulePassManager MPM(DebugLogging); + // Place pseudo probe instrumentation as the first pass of the pipeline to + // minimize the impact of optimization changes. + if (PGOOpt && PGOOpt->PseudoProbeForProfiling && + Phase != ThinLTOPhase::PostLink) + MPM.addPass(SampleProfileProbePass(TM)); + bool HasSampleProfile = PGOOpt && (PGOOpt->Action == PGOOptions::SampleUse); // In ThinLTO mode, when flattened profile is used, all the available @@ -1254,7 +1260,7 @@ for (auto &C : PipelineStartEPCallbacks) C(MPM); - if (PGOOpt && PGOOpt->SamplePGOSupport) + if (PGOOpt && PGOOpt->DebugInfoForProfiling) MPM.addPass(createModuleToFunctionPassAdaptor(AddDiscriminatorsPass())); // Add the core simplification pipeline. @@ -1278,7 +1284,7 @@ // Force any function attributes we want the rest of the pipeline to observe. MPM.addPass(ForceFunctionAttrsPass()); - if (PGOOpt && PGOOpt->SamplePGOSupport) + if (PGOOpt && PGOOpt->DebugInfoForProfiling) MPM.addPass(createModuleToFunctionPassAdaptor(AddDiscriminatorsPass())); // Apply module pipeline start EP callback.