[flang-commits] [clang] [flang] [flang][Driver] Preliminary support for -ftime-report (PR #122894)
via flang-commits
flang-commits at lists.llvm.org
Tue Jan 14 04:14:31 PST 2025
https://github.com/macurtis-amd created https://github.com/llvm/llvm-project/pull/122894
The behavior is not entirely consistent with that of clang for the moment since detailed timing information on the LLVM IR optimization and code generation passes is not provided. The -ftime-report= option is also not enabled since that is only relevant for information about the LLVM IR passes. However, some code to handle that option has been included, to make it easier to support the option when the issues blocking it are resolved. A FortranSupport library has been created that is intended to mirror the LLVM and MLIR support libraries.
>From 584e352225b68fbf5105290ad605d7c855fa6e37 Mon Sep 17 00:00:00 2001
From: Tarun Prabhu <tarun.prabhu at gmail.com>
Date: Wed, 4 Sep 2024 10:24:31 -0600
Subject: [PATCH] [flang][Driver] Preliminary support for -ftime-report
The behavior is not entirely consistent with that of clang for the moment
since detailed timing information on the LLVM IR optimization and code
generation passes is not provided. The -ftime-report= option is also not enabled
since that is only relevant for information about the LLVM IR passes. However,
some code to handle that option has been included, to make it easier to support
the option when the issues blocking it are resolved. A FortranSupport library
has been created that is intended to mirror the LLVM and MLIR support libraries.
---
clang/include/clang/Driver/Options.td | 2 +-
clang/lib/Driver/ToolChains/Flang.cpp | 10 +--
.../include/flang/Frontend/CompilerInstance.h | 58 +++++++++++++++
.../flang/Frontend/CompilerInvocation.h | 7 ++
flang/include/flang/Support/StringOstream.h | 32 +++++++++
flang/include/flang/Support/Timing.h | 27 +++++++
flang/lib/CMakeLists.txt | 1 +
flang/lib/Frontend/CMakeLists.txt | 1 +
flang/lib/Frontend/CompilerInstance.cpp | 52 +++++++++++++-
flang/lib/Frontend/CompilerInvocation.cpp | 4 ++
flang/lib/Frontend/FrontendActions.cpp | 72 +++++++++++++++++--
flang/lib/Support/CMakeLists.txt | 9 +++
flang/lib/Support/Timing.cpp | 67 +++++++++++++++++
flang/test/Driver/time-report.f90 | 22 ++++++
14 files changed, 352 insertions(+), 12 deletions(-)
create mode 100644 flang/include/flang/Support/StringOstream.h
create mode 100644 flang/include/flang/Support/Timing.h
create mode 100644 flang/lib/Support/CMakeLists.txt
create mode 100644 flang/lib/Support/Timing.cpp
create mode 100644 flang/test/Driver/time-report.f90
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 7c41d38e00a492..2721c1b5d8dc55 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4108,7 +4108,7 @@ defm ms_tls_guards : BoolFOption<"ms-tls-guards",
"Do not emit code to perform on-demand initialization of thread-local variables">,
PosFlag<SetTrue>>;
def ftime_report : Flag<["-"], "ftime-report">, Group<f_Group>,
- Visibility<[ClangOption, CC1Option]>,
+ Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>,
MarshallingInfoFlag<CodeGenOpts<"TimePasses">>;
def ftime_report_EQ: Joined<["-"], "ftime-report=">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>, Values<"per-pass,per-pass-run">,
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 75b10e88371ae7..a7d0cc99f27d2d 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -150,10 +150,12 @@ void Flang::addCodegenOptions(const ArgList &Args,
if (shouldLoopVersion(Args))
CmdArgs.push_back("-fversion-loops-for-stride");
- Args.addAllArgs(CmdArgs, {options::OPT_flang_experimental_hlfir,
- options::OPT_flang_deprecated_no_hlfir,
- options::OPT_fno_ppc_native_vec_elem_order,
- options::OPT_fppc_native_vec_elem_order});
+ Args.addAllArgs(CmdArgs,
+ {options::OPT_flang_experimental_hlfir,
+ options::OPT_flang_deprecated_no_hlfir,
+ options::OPT_fno_ppc_native_vec_elem_order,
+ options::OPT_fppc_native_vec_elem_order,
+ options::OPT_ftime_report, options::OPT_ftime_report_EQ});
}
void Flang::addPicOptions(const ArgList &Args, ArgStringList &CmdArgs) const {
diff --git a/flang/include/flang/Frontend/CompilerInstance.h b/flang/include/flang/Frontend/CompilerInstance.h
index 4fcc59f7cf577b..509c9f4b9e91aa 100644
--- a/flang/include/flang/Frontend/CompilerInstance.h
+++ b/flang/include/flang/Frontend/CompilerInstance.h
@@ -20,6 +20,7 @@
#include "flang/Parser/provenance.h"
#include "flang/Semantics/runtime-type-info.h"
#include "flang/Semantics/semantics.h"
+#include "flang/Support/StringOstream.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
@@ -85,6 +86,27 @@ class CompilerInstance {
/// facilitate this. It is optional and will normally be just a nullptr.
std::unique_ptr<llvm::raw_pwrite_stream> outputStream;
+ /// @name Timing
+ /// Objects needed when timing is enabled.
+ /// @{
+ /// The timing manager.
+ mlir::DefaultTimingManager timingMgr;
+
+ /// The root of the timingScope. This will be reset in @ref executeAction if
+ /// timers have been enabled.
+ mlir::TimingScope timingScopeRoot;
+
+ /// @name Timing stream
+ /// The output streams to capture the timing. Three different streams are
+ /// needed because the timing classes all work slightly differently. We create
+ /// these streams so we have control over when and how the timing is
+ /// displayed. Otherwise, the timing is only displayed when the corresponding
+ /// managers/timers go out of scope.
+ std::unique_ptr<Fortran::support::string_ostream> timingStreamMLIR;
+ std::unique_ptr<Fortran::support::string_ostream> timingStreamLLVM;
+ std::unique_ptr<Fortran::support::string_ostream> timingStreamCodeGen;
+ /// @}
+
public:
explicit CompilerInstance();
@@ -254,6 +276,42 @@ class CompilerInstance {
/// Produces the string which represents target feature
std::string getTargetFeatures();
+ /// {
+ /// @name Timing
+ /// @{
+ bool isTimingEnabled() const { return timingMgr.isEnabled(); }
+
+ mlir::DefaultTimingManager &getTimingManager() { return timingMgr; }
+ const mlir::DefaultTimingManager &getTimingManager() const {
+ return timingMgr;
+ }
+
+ mlir::TimingScope &getTimingScopeRoot() { return timingScopeRoot; }
+ const mlir::TimingScope &getTimingScopeRoot() const {
+ return timingScopeRoot;
+ }
+
+ /// Get the timing stream for the MLIR pass manager.
+ llvm::raw_ostream &getTimingStreamMLIR() {
+ assert(timingStreamMLIR && "Timing stream for MLIR was not set");
+ return *timingStreamMLIR;
+ }
+
+ /// Get the timing stream for the new LLVM pass manager.
+ llvm::raw_ostream &getTimingStreamLLVM() {
+ assert(timingStreamLLVM && "Timing stream for LLVM was not set");
+ return *timingStreamLLVM;
+ }
+
+ /// Get the timing stream fro the legacy LLVM pass manager.
+ /// NOTE: If the codegen is updated to use the new pass manager, this should
+ /// no longer be needed.
+ llvm::raw_ostream &getTimingStreamCodeGen() {
+ assert(timingStreamCodeGen && "Timing stream for codegen was not set");
+ return *timingStreamCodeGen;
+ }
+ /// @}
+
private:
/// Create a new output file
///
diff --git a/flang/include/flang/Frontend/CompilerInvocation.h b/flang/include/flang/Frontend/CompilerInvocation.h
index 50d908d0832024..7d3f0bdf2e510e 100644
--- a/flang/include/flang/Frontend/CompilerInvocation.h
+++ b/flang/include/flang/Frontend/CompilerInvocation.h
@@ -21,6 +21,7 @@
#include "flang/Lower/LoweringOptions.h"
#include "flang/Parser/parsing.h"
#include "flang/Semantics/semantics.h"
+#include "mlir/Support/Timing.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "llvm/Option/ArgList.h"
@@ -143,6 +144,10 @@ class CompilerInvocation : public CompilerInvocationBase {
},
};
+ /// Whether to time the invocation. Set when -ftime-report or -ftime-report=
+ /// is enabled.
+ bool enableTimers;
+
public:
CompilerInvocation() = default;
@@ -222,6 +227,8 @@ class CompilerInvocation : public CompilerInvocationBase {
return defaultKinds;
}
+ bool getEnableTimers() const { return enableTimers; }
+
/// Create a compiler invocation from a list of input options.
/// \returns true on success.
/// \returns false if an error was encountered while parsing the arguments
diff --git a/flang/include/flang/Support/StringOstream.h b/flang/include/flang/Support/StringOstream.h
new file mode 100644
index 00000000000000..2e5c87eae058c6
--- /dev/null
+++ b/flang/include/flang/Support/StringOstream.h
@@ -0,0 +1,32 @@
+//===-- CompilerInstance.h - Flang Compiler Instance ------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_SUPPORT_STRINGOSTREAM_H
+#define FORTRAN_SUPPORT_STRINGOSTREAM_H
+
+#include <llvm/Support/raw_ostream.h>
+
+namespace Fortran::support {
+
+/// Helper class to maintain both the an llvm::raw_string_ostream object and
+/// its associated buffer.
+class string_ostream : public llvm::raw_string_ostream {
+private:
+ std::string buf;
+
+public:
+ string_ostream() : llvm::raw_string_ostream(buf) {}
+};
+
+} // namespace Fortran::support
+
+#endif // FORTRAN_SUPPORT_STRINGOSTREAM_H
diff --git a/flang/include/flang/Support/Timing.h b/flang/include/flang/Support/Timing.h
new file mode 100644
index 00000000000000..75ba2a8d85f39f
--- /dev/null
+++ b/flang/include/flang/Support/Timing.h
@@ -0,0 +1,27 @@
+//===- Timing.h - Execution time measurement facilities ---------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Facilities to measure and provide statistics on execution time.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_SUPPORT_TIMING_H
+#define FORTRAN_SUPPORT_TIMING_H
+
+#include "mlir/Support/Timing.h"
+
+namespace Fortran::support {
+
+/// Create a strategy to render the captured times in plain text. This is
+/// intended to be passed to a TimingManager.
+std::unique_ptr<mlir::OutputStrategy> createTimingFormatterText(
+ llvm::raw_ostream &os);
+
+} // namespace Fortran::support
+
+#endif // FORTRAN_SUPPORT_TIMING_H
diff --git a/flang/lib/CMakeLists.txt b/flang/lib/CMakeLists.txt
index f41d4df1f07e3c..2182e845b6a793 100644
--- a/flang/lib/CMakeLists.txt
+++ b/flang/lib/CMakeLists.txt
@@ -4,6 +4,7 @@ add_subdirectory(Decimal)
add_subdirectory(Lower)
add_subdirectory(Parser)
add_subdirectory(Semantics)
+add_subdirectory(Support)
add_subdirectory(Frontend)
add_subdirectory(FrontendTool)
diff --git a/flang/lib/Frontend/CMakeLists.txt b/flang/lib/Frontend/CMakeLists.txt
index e954800c3b88b0..1b90fe826af6c9 100644
--- a/flang/lib/Frontend/CMakeLists.txt
+++ b/flang/lib/Frontend/CMakeLists.txt
@@ -29,6 +29,7 @@ add_flang_library(flangFrontend
FortranEvaluate
FortranCommon
FortranLower
+ FortranSupport
FIRDialect
FIRDialectSupport
FIRSupport
diff --git a/flang/lib/Frontend/CompilerInstance.cpp b/flang/lib/Frontend/CompilerInstance.cpp
index 35c2ae3c73e69e..298790bae66558 100644
--- a/flang/lib/Frontend/CompilerInstance.cpp
+++ b/flang/lib/Frontend/CompilerInstance.cpp
@@ -17,9 +17,12 @@
#include "flang/Parser/parsing.h"
#include "flang/Parser/provenance.h"
#include "flang/Semantics/semantics.h"
+#include "flang/Support/Timing.h"
+#include "mlir/Support/RawOstreamExtras.h"
#include "clang/Basic/DiagnosticFrontend.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Pass.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
@@ -147,7 +150,7 @@ void CompilerInstance::clearOutputFiles(bool eraseFiles) {
}
bool CompilerInstance::executeAction(FrontendAction &act) {
- auto &invoc = this->getInvocation();
+ CompilerInvocation &invoc = this->getInvocation();
llvm::Triple targetTriple{llvm::Triple(invoc.getTargetOpts().triple)};
if (targetTriple.getArch() == llvm::Triple::ArchType::x86_64) {
@@ -167,6 +170,25 @@ bool CompilerInstance::executeAction(FrontendAction &act) {
// Set options controlling lowering to FIR.
invoc.setLoweringOptions();
+ if (invoc.getEnableTimers()) {
+ llvm::TimePassesIsEnabled = true;
+
+ timingStreamMLIR = std::make_unique<Fortran::support::string_ostream>();
+ timingStreamLLVM = std::make_unique<Fortran::support::string_ostream>();
+ timingStreamCodeGen = std::make_unique<Fortran::support::string_ostream>();
+
+ timingMgr.setEnabled(true);
+ timingMgr.setDisplayMode(mlir::DefaultTimingManager::DisplayMode::Tree);
+ timingMgr.setOutput(
+ Fortran::support::createTimingFormatterText(*timingStreamMLIR));
+
+ // Creating a new TimingScope will automatically start the timer. Since this
+ // is the top-level timer, this is ok because it will end up capturing the
+ // time for all the bookkeeping and other tasks that take place between
+ // parsing, lowering etc. for which finer-grained timers will be created.
+ timingScopeRoot = timingMgr.getRootScope();
+ }
+
// Run the frontend action `act` for every input file.
for (const FrontendInputFile &fif : getFrontendOpts().inputs) {
if (act.beginSourceFile(*this, fif)) {
@@ -176,6 +198,34 @@ bool CompilerInstance::executeAction(FrontendAction &act) {
act.endSourceFile();
}
}
+
+ if (timingMgr.isEnabled()) {
+ timingScopeRoot.stop();
+
+ // Write the timings to the associated output stream and clear all timers.
+ // We need to provide another stream because the TimingManager will attempt
+ // to print in its destructor even if it has been cleared. By the time that
+ // destructor runs, the output streams will have been destroyed, so give it
+ // a null stream.
+ timingMgr.print();
+ timingMgr.setOutput(
+ Fortran::support::createTimingFormatterText(mlir::thread_safe_nulls()));
+
+ // This prints the timings in "reverse" order, starting from code
+ // generation, followed by LLVM-IR optimizations, then MLIR optimizations
+ // and transformations and the frontend. If any of the steps are disabled,
+ // for instance because code generation was not performed, the strings
+ // will be empty.
+ if (!timingStreamCodeGen->str().empty())
+ llvm::errs() << timingStreamCodeGen->str() << "\n";
+
+ if (!timingStreamLLVM->str().empty())
+ llvm::errs() << timingStreamLLVM->str() << "\n";
+
+ if (!timingStreamMLIR->str().empty())
+ llvm::errs() << timingStreamMLIR->str() << "\n";
+ }
+
return !getDiagnostics().getClient()->getNumErrors();
}
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 340efb1c63a5e5..5e7127313c1335 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1436,6 +1436,10 @@ bool CompilerInvocation::createFromArgs(
}
}
+ // Process the timing-related options.
+ if (args.hasArg(clang::driver::options::OPT_ftime_report))
+ invoc.enableTimers = true;
+
invoc.setArgv0(argv0);
return success;
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index 310cd650349c7a..52a18d59c7cda5 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -84,6 +84,15 @@ extern cl::opt<bool> PrintPipelinePasses;
using namespace Fortran::frontend;
+constexpr llvm::StringLiteral timingIdParse = "Parse";
+constexpr llvm::StringLiteral timingIdMLIRGen = "MLIR generation";
+constexpr llvm::StringLiteral timingIdMLIRPasses =
+ "MLIR translation/optimization";
+constexpr llvm::StringLiteral timingIdLLVMIRGen = "LLVM IR generation";
+constexpr llvm::StringLiteral timingIdLLVMIRPasses = "LLVM IR optimizations";
+constexpr llvm::StringLiteral timingIdBackend =
+ "Assembly/Object code generation";
+
// Declare plugin extension function declarations.
#define HANDLE_EXTENSION(Ext) \
llvm::PassPluginLibraryInfo get##Ext##PluginInfo();
@@ -227,6 +236,14 @@ static void addAMDGPUSpecificMLIRItems(mlir::ModuleOp mlirModule,
bool CodeGenAction::beginSourceFileAction() {
llvmCtx = std::make_unique<llvm::LLVMContext>();
CompilerInstance &ci = this->getInstance();
+ mlir::DefaultTimingManager &timingMgr = ci.getTimingManager();
+ mlir::TimingScope &timingScopeRoot = ci.getTimingScopeRoot();
+
+ // This will provide timing information even when the input is an LLVM IR or
+ // MLIR file. That is fine because those do have to be parsed, so the label
+ // is still accurate.
+ mlir::TimingScope timingScopeParse = timingScopeRoot.nest(
+ mlir::TimingIdentifier::get(timingIdParse, timingMgr));
// If the input is an LLVM file, just parse it and return.
if (this->getCurrentInput().getKind().getLanguage() == Language::LLVM_IR) {
@@ -288,6 +305,10 @@ bool CodeGenAction::beginSourceFileAction() {
if (!res)
return res;
+ timingScopeParse.stop();
+ mlir::TimingScope timingScopeMLIRGen = timingScopeRoot.nest(
+ mlir::TimingIdentifier::get(timingIdMLIRGen, timingMgr));
+
// Create a LoweringBridge
const common::IntrinsicTypeDefaultKinds &defKinds =
ci.getSemanticsContext().defaultKinds();
@@ -322,6 +343,7 @@ bool CodeGenAction::beginSourceFileAction() {
// constants etc.
addDependentLibs(*mlirModule, ci);
addAMDGPUSpecificMLIRItems(*mlirModule, ci);
+ timingScopeMLIRGen.stop();
// run the default passes.
mlir::PassManager pm((*mlirModule)->getName(),
@@ -344,6 +366,7 @@ bool CodeGenAction::beginSourceFileAction() {
pm.enableVerifier(/*verifyPasses=*/true);
pm.addPass(std::make_unique<Fortran::lower::VerifierPass>());
+ pm.enableTiming(timingScopeMLIRGen);
if (mlir::failed(pm.run(*mlirModule))) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
@@ -352,6 +375,7 @@ bool CodeGenAction::beginSourceFileAction() {
ci.getDiagnostics().Report(diagID);
return false;
}
+ timingScopeMLIRGen.stop();
// Print initial full MLIR module, before lowering or transformations, if
// -save-temps has been specified.
@@ -704,8 +728,10 @@ void CodeGenAction::lowerHLFIRToFIR() {
assert(mlirModule && "The MLIR module has not been generated yet.");
CompilerInstance &ci = this->getInstance();
- auto opts = ci.getInvocation().getCodeGenOpts();
+ const CodeGenOptions &opts = ci.getInvocation().getCodeGenOpts();
llvm::OptimizationLevel level = mapToLevel(opts);
+ mlir::DefaultTimingManager &timingMgr = ci.getTimingManager();
+ mlir::TimingScope &timingScopeRoot = ci.getTimingScopeRoot();
fir::support::loadDialects(*mlirCtx);
@@ -724,6 +750,9 @@ void CodeGenAction::lowerHLFIRToFIR() {
level);
(void)mlir::applyPassManagerCLOptions(pm);
+ mlir::TimingScope timingScopeMLIRPasses = timingScopeRoot.nest(
+ mlir::TimingIdentifier::get(timingIdMLIRPasses, timingMgr));
+ pm.enableTiming(timingScopeMLIRPasses);
if (!mlir::succeeded(pm.run(*mlirModule))) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Lowering to FIR failed");
@@ -808,9 +837,12 @@ void CodeGenAction::generateLLVMIR() {
assert(mlirModule && "The MLIR module has not been generated yet.");
CompilerInstance &ci = this->getInstance();
- auto opts = ci.getInvocation().getCodeGenOpts();
- auto mathOpts = ci.getInvocation().getLoweringOpts().getMathOptions();
+ CompilerInvocation &invoc = ci.getInvocation();
+ const CodeGenOptions &opts = invoc.getCodeGenOpts();
+ const auto &mathOpts = invoc.getLoweringOpts().getMathOptions();
llvm::OptimizationLevel level = mapToLevel(opts);
+ mlir::DefaultTimingManager &timingMgr = ci.getTimingManager();
+ mlir::TimingScope &timingScopeRoot = ci.getTimingScopeRoot();
fir::support::loadDialects(*mlirCtx);
mlir::DialectRegistry registry;
@@ -846,11 +878,15 @@ void CodeGenAction::generateLLVMIR() {
(void)mlir::applyPassManagerCLOptions(pm);
// run the pass manager
+ mlir::TimingScope timingScopeMLIRPasses = timingScopeRoot.nest(
+ mlir::TimingIdentifier::get(timingIdMLIRPasses, timingMgr));
+ pm.enableTiming(timingScopeMLIRPasses);
if (!mlir::succeeded(pm.run(*mlirModule))) {
unsigned diagID = ci.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, "Lowering to LLVM IR failed");
ci.getDiagnostics().Report(diagID);
}
+ timingScopeMLIRPasses.stop();
// Print final MLIR module, just before translation into LLVM IR, if
// -save-temps has been specified.
@@ -863,6 +899,8 @@ void CodeGenAction::generateLLVMIR() {
}
// Translate to LLVM IR
+ mlir::TimingScope timingScopeLLVMIRGen = timingScopeRoot.nest(
+ mlir::TimingIdentifier::get(timingIdLLVMIRGen, timingMgr));
std::optional<llvm::StringRef> moduleName = mlirModule->getName();
llvmModule = mlir::translateModuleToLLVMIR(
*mlirModule, *llvmCtx, moduleName ? *moduleName : "FIRModule");
@@ -969,11 +1007,12 @@ static void generateMachineCodeOrAssemblyImpl(clang::DiagnosticsEngine &diags,
}
void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
- auto opts = getInstance().getInvocation().getCodeGenOpts();
- auto &diags = getInstance().getDiagnostics();
+ CompilerInstance &ci = getInstance();
+ const CodeGenOptions &opts = ci.getInvocation().getCodeGenOpts();
+ clang::DiagnosticsEngine &diags = ci.getDiagnostics();
llvm::OptimizationLevel level = mapToLevel(opts);
- llvm::TargetMachine *targetMachine = &getInstance().getTargetMachine();
+ llvm::TargetMachine *targetMachine = &ci.getTargetMachine();
// Create the analysis managers.
llvm::LoopAnalysisManager lam;
llvm::FunctionAnalysisManager fam;
@@ -987,6 +1026,8 @@ void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
llvm::StandardInstrumentations si(llvmModule->getContext(),
opts.DebugPassManager);
si.registerCallbacks(pic, &mam);
+ if (ci.isTimingEnabled())
+ si.getTimePasses().setOutStream(ci.getTimingStreamLLVM());
llvm::PassBuilder pb(targetMachine, pto, pgoOpt, &pic);
// Attempt to load pass plugins and register their callbacks with PB.
@@ -1049,6 +1090,10 @@ void CodeGenAction::runOptimizationPipeline(llvm::raw_pwrite_stream &os) {
// Run the passes.
mpm.run(*llvmModule, mam);
+ // Print the timers to the associated output stream and reset them.
+ if (ci.isTimingEnabled())
+ si.getTimePasses().print();
+
// Cleanup
delete tlii;
}
@@ -1271,6 +1316,8 @@ void CodeGenAction::executeAction() {
const CodeGenOptions &codeGenOpts = ci.getInvocation().getCodeGenOpts();
Fortran::lower::LoweringOptions &loweringOpts =
ci.getInvocation().getLoweringOpts();
+ mlir::DefaultTimingManager &timingMgr = ci.getTimingManager();
+ mlir::TimingScope &timingScopeRoot = ci.getTimingScopeRoot();
// If the output stream is a file, generate it and define the corresponding
// output stream. If a pre-defined output stream is available, we will use
@@ -1316,6 +1363,11 @@ void CodeGenAction::executeAction() {
if (!llvmModule)
generateLLVMIR();
+ // This will already have been started in generateLLVMIR(). But we need to
+ // continue operating on the module, so we continue timing it.
+ mlir::TimingScope timingScopeLLVMIRGen = timingScopeRoot.nest(
+ mlir::TimingIdentifier::get(timingIdLLVMIRGen, timingMgr));
+
// If generating the LLVM module failed, abort! No need for further error
// reporting since generateLLVMIR() does this already.
if (!llvmModule)
@@ -1345,6 +1397,7 @@ void CodeGenAction::executeAction() {
// Embed offload objects specified with -fembed-offload-object
if (!codeGenOpts.OffloadObjects.empty())
embedOffloadObjects();
+ timingScopeLLVMIRGen.stop();
BackendRemarkConsumer remarkConsumer(diags, codeGenOpts);
@@ -1373,7 +1426,10 @@ void CodeGenAction::executeAction() {
}
// Run LLVM's middle-end (i.e. the optimizer).
+ mlir::TimingScope timingScopeLLVMIRPasses = timingScopeRoot.nest(
+ mlir::TimingIdentifier::get(timingIdLLVMIRPasses, timingMgr));
runOptimizationPipeline(ci.isOutputStreamNull() ? *os : ci.getOutputStream());
+ timingScopeLLVMIRPasses.stop();
if (action == BackendActionTy::Backend_EmitLL ||
action == BackendActionTy::Backend_EmitBC) {
@@ -1382,11 +1438,15 @@ void CodeGenAction::executeAction() {
}
// Run LLVM's backend and generate either assembly or machine code
+ mlir::TimingScope timingScopeBackend = timingScopeRoot.nest(
+ mlir::TimingIdentifier::get(timingIdBackend, timingMgr));
if (action == BackendActionTy::Backend_EmitAssembly ||
action == BackendActionTy::Backend_EmitObj) {
generateMachineCodeOrAssemblyImpl(
diags, targetMachine, action, *llvmModule, codeGenOpts,
ci.isOutputStreamNull() ? *os : ci.getOutputStream());
+ if (timingMgr.isEnabled())
+ llvm::reportAndResetTimings(&ci.getTimingStreamCodeGen());
return;
}
}
diff --git a/flang/lib/Support/CMakeLists.txt b/flang/lib/Support/CMakeLists.txt
new file mode 100644
index 00000000000000..9c7887aecafbd6
--- /dev/null
+++ b/flang/lib/Support/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_flang_library(FortranSupport
+ Timing.cpp
+
+ LINK_LIBS
+ MLIRSupport
+
+ LINK_COMPONENTS
+ Support
+)
diff --git a/flang/lib/Support/Timing.cpp b/flang/lib/Support/Timing.cpp
new file mode 100644
index 00000000000000..ee8309a950eec8
--- /dev/null
+++ b/flang/lib/Support/Timing.cpp
@@ -0,0 +1,67 @@
+//===- Timing.cpp - Execution time measurement facilities -----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Facilities to measure and provide statistics on execution time.
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Support/Timing.h"
+#include "llvm/Support/Format.h"
+
+class OutputStrategyText : public mlir::OutputStrategy {
+protected:
+ static constexpr llvm::StringLiteral header = "Flang execution timing report";
+
+public:
+ OutputStrategyText(llvm::raw_ostream &os) : mlir::OutputStrategy(os) {}
+
+ void printHeader(const mlir::TimeRecord &total) override {
+ // Figure out how many spaces to description name.
+ unsigned padding = (80 - header.size()) / 2;
+ os << "===" << std::string(73, '-') << "===\n";
+ os.indent(padding) << header << '\n';
+ os << "===" << std::string(73, '-') << "===\n";
+
+ // Print the total time followed by the section headers.
+ os << llvm::format(" Total Execution Time: %.4f seconds\n\n", total.wall);
+ os << " ----User Time---- ----Wall Time---- ----Name----\n";
+ }
+
+ void printFooter() override { os.flush(); }
+
+ void printTime(
+ const mlir::TimeRecord &time, const mlir::TimeRecord &total) override {
+ os << llvm::format(
+ " %8.4f (%5.1f%%)", time.user, 100.0 * time.user / total.user);
+ os << llvm::format(
+ " %8.4f (%5.1f%%) ", time.wall, 100.0 * time.wall / total.wall);
+ }
+
+ void printListEntry(llvm::StringRef name, const mlir::TimeRecord &time,
+ const mlir::TimeRecord &total, bool lastEntry) override {
+ printTime(time, total);
+ os << name << "\n";
+ }
+
+ void printTreeEntry(unsigned indent, llvm::StringRef name,
+ const mlir::TimeRecord &time, const mlir::TimeRecord &total) override {
+ printTime(time, total);
+ os.indent(indent) << name << "\n";
+ }
+
+ void printTreeEntryEnd(unsigned indent, bool lastEntry) override {}
+};
+
+namespace Fortran::support {
+
+std::unique_ptr<mlir::OutputStrategy> createTimingFormatterText(
+ llvm::raw_ostream &os) {
+ return std::make_unique<OutputStrategyText>(os);
+}
+
+} // namespace Fortran::support
diff --git a/flang/test/Driver/time-report.f90 b/flang/test/Driver/time-report.f90
new file mode 100644
index 00000000000000..3f6e1e9f87d9a1
--- /dev/null
+++ b/flang/test/Driver/time-report.f90
@@ -0,0 +1,22 @@
+! Check that -ftime-report flag is passed as-is to fc1. The value of the flag
+! is only checked there. This behavior intentionally mirrors that of clang.
+!
+! RUN: %flang -### -c -ftime-report %s 2>&1 | FileCheck %s --check-prefix=CHECK-DRIVER
+
+! TODO: Currently, detailed timing of LLVM IR optimization and code generation
+! passes is not supported. When that is done, add more checks here to make sure
+! the output is as expected.
+
+! RUN: %flang -c -ftime-report -O0 %s 2>&1 | FileCheck %s --check-prefix=CHECK-COMMON
+! RUN: %flang -c -ftime-report -O1 %s 2>&1 | FileCheck %s --check-prefix=CHECK-COMMON
+
+! CHECK-DRIVER: "-ftime-report"
+
+! CHECK-COMMON: Flang execution timing report
+! CHECK-COMMON: MLIR generation
+! CHECK-COMMON: MLIR translation/optimization
+! CHECK-COMMON: LLVM IR generation
+! CHECK-COMMON: LLVM IR optimizations
+! CHECK-COMMON: Assembly/Object code generation
+
+end program
More information about the flang-commits
mailing list