Skip to content

Commit 2f4a850

Browse files
author
Tarun Prabhu
committed
[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.
1 parent 216ba6b commit 2f4a850

File tree

15 files changed

+390
-9
lines changed

15 files changed

+390
-9
lines changed

clang/include/clang/Driver/Options.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4012,7 +4012,7 @@ defm threadsafe_statics : BoolFOption<"threadsafe-statics",
40124012
"Do not emit code to make initialization of local statics thread safe">,
40134013
PosFlag<SetTrue>>;
40144014
def ftime_report : Flag<["-"], "ftime-report">, Group<f_Group>,
4015-
Visibility<[ClangOption, CC1Option]>,
4015+
Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>,
40164016
MarshallingInfoFlag<CodeGenOpts<"TimePasses">>;
40174017
def ftime_report_EQ: Joined<["-"], "ftime-report=">, Group<f_Group>,
40184018
Visibility<[ClangOption, CC1Option]>, Values<"per-pass,per-pass-run">,

clang/lib/Driver/ToolChains/Flang.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@ void Flang::addCodegenOptions(const ArgList &Args,
150150
options::OPT_flang_deprecated_no_hlfir,
151151
options::OPT_flang_experimental_integer_overflow,
152152
options::OPT_fno_ppc_native_vec_elem_order,
153-
options::OPT_fppc_native_vec_elem_order});
153+
options::OPT_fppc_native_vec_elem_order,
154+
options::OPT_ftime_report,
155+
options::OPT_ftime_report_EQ});
154156
}
155157

156158
void Flang::addPicOptions(const ArgList &Args, ArgStringList &CmdArgs) const {

flang/include/flang/Frontend/CompilerInstance.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "flang/Parser/provenance.h"
2121
#include "flang/Semantics/runtime-type-info.h"
2222
#include "flang/Semantics/semantics.h"
23+
#include "flang/Support/StringOstream.h"
2324
#include "llvm/Support/raw_ostream.h"
2425
#include "llvm/Target/TargetMachine.h"
2526

@@ -85,6 +86,27 @@ class CompilerInstance {
8586
/// facilitate this. It is optional and will normally be just a nullptr.
8687
std::unique_ptr<llvm::raw_pwrite_stream> outputStream;
8788

89+
/// @name Timing
90+
/// Objects needed when timing is enabled.
91+
/// @{
92+
/// The timing manager.
93+
mlir::DefaultTimingManager timingMgr;
94+
95+
/// The root of the timingScope. This will be reset in @ref executeAction if
96+
/// timers have been enabled.
97+
mlir::TimingScope timingScopeRoot;
98+
99+
/// @name Timing stream
100+
/// The output streams to capture the timing. Three different streams are
101+
/// needed because the timing classes all work slightly differently. We create
102+
/// these streams so we have control over when and how the timing is
103+
/// displayed. Otherwise, the timing is only displayed when the corresponding
104+
/// managers/timers go out of scope.
105+
std::unique_ptr<Fortran::support::string_ostream> timingStreamMLIR;
106+
std::unique_ptr<Fortran::support::string_ostream> timingStreamLLVM;
107+
std::unique_ptr<Fortran::support::string_ostream> timingStreamCodeGen;
108+
/// @}
109+
88110
public:
89111
explicit CompilerInstance();
90112

@@ -254,6 +276,43 @@ class CompilerInstance {
254276
/// Produces the string which represents target feature
255277
std::string getTargetFeatures();
256278

279+
/// {
280+
/// @name Timing
281+
/// @{
282+
bool isTimingEnabled() { return timingMgr.isEnabled(); }
283+
bool isTimingEnabled() const { return timingMgr.isEnabled(); }
284+
285+
mlir::DefaultTimingManager &getTimingManager() { return timingMgr; }
286+
const mlir::DefaultTimingManager &getTimingManager() const {
287+
return timingMgr;
288+
}
289+
290+
mlir::TimingScope &getTimingScopeRoot() { return timingScopeRoot; }
291+
const mlir::TimingScope &getTimingScopeRoot() const {
292+
return timingScopeRoot;
293+
}
294+
295+
/// Get the timing stream for the MLIR pass manager.
296+
llvm::raw_ostream &getTimingStreamMLIR() {
297+
assert(timingStreamMLIR && "Timing stream for MLIR was not set");
298+
return *timingStreamMLIR;
299+
}
300+
301+
/// Get the timing stream for the new LLVM pass manager.
302+
llvm::raw_ostream &getTimingStreamLLVM() {
303+
assert(timingStreamLLVM && "Timing stream for LLVM was not set");
304+
return *timingStreamLLVM;
305+
}
306+
307+
/// Get the timing stream fro the legacy LLVM pass manager.
308+
/// NOTE: If the codegen is updated to use the new pass manager, this should
309+
/// no longer be needed.
310+
llvm::raw_ostream &getTimingStreamCodeGen() {
311+
assert(timingStreamCodeGen && "Timing stream for codegen was not set");
312+
return *timingStreamCodeGen;
313+
}
314+
/// @}
315+
257316
private:
258317
/// Create a new output file
259318
///

flang/include/flang/Frontend/CompilerInvocation.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "flang/Lower/LoweringOptions.h"
2222
#include "flang/Parser/parsing.h"
2323
#include "flang/Semantics/semantics.h"
24+
#include "mlir/Support/Timing.h"
2425
#include "clang/Basic/Diagnostic.h"
2526
#include "clang/Basic/DiagnosticOptions.h"
2627
#include "llvm/Option/ArgList.h"
@@ -143,6 +144,14 @@ class CompilerInvocation : public CompilerInvocationBase {
143144
},
144145
};
145146

147+
/// Whether to time the invocation. Set when -ftime-report or -ftime-report=
148+
/// is enabled.
149+
bool enableTimers;
150+
151+
/// Whether to report the timing of each run of an LLVM pass. Set when
152+
/// -ftime-report=per-pass-run is enabled.
153+
bool timeLLVMPassesPerRun;
154+
146155
public:
147156
CompilerInvocation() = default;
148157

@@ -222,6 +231,12 @@ class CompilerInvocation : public CompilerInvocationBase {
222231
return defaultKinds;
223232
}
224233

234+
bool getEnableTimers() { return enableTimers; }
235+
bool getEnableTimers() const { return enableTimers; }
236+
237+
bool getTimeLLVMPassesPerRun() { return timeLLVMPassesPerRun; }
238+
bool getTimeLLVMPassesPerRun() const { return timeLLVMPassesPerRun; }
239+
225240
/// Create a compiler invocation from a list of input options.
226241
/// \returns true on success.
227242
/// \returns false if an error was encountered while parsing the arguments
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===-- CompilerInstance.h - Flang Compiler Instance ------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef FORTRAN_SUPPORT_STRINGOSTREAM_H
14+
#define FORTRAN_SUPPORT_STRINGOSTREAM_H
15+
16+
#include <llvm/Support/raw_ostream.h>
17+
18+
namespace Fortran::support {
19+
20+
/// Helper class to maintain both the an llvm::raw_string_ostream object and
21+
/// its associated buffer.
22+
class string_ostream : public llvm::raw_string_ostream {
23+
private:
24+
std::string buf;
25+
26+
public:
27+
string_ostream() : llvm::raw_string_ostream(buf) {}
28+
};
29+
30+
} // namespace Fortran::support
31+
32+
#endif // FORTRAN_SUPPORT_STRINGOSTREAM_H
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//===- Timing.h - Execution time measurement facilities ---------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// Facilities to measure and provide statistics on execution time.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef FORTRAN_SUPPORT_TIMING_H
14+
#define FORTRAN_SUPPORT_TIMING_H
15+
16+
#include "mlir/Support/Timing.h"
17+
18+
namespace Fortran::support {
19+
20+
/// Create a strategy to render the captured times in plain text. This is
21+
/// intended to be passed to a TimingManager.
22+
std::unique_ptr<mlir::OutputStrategy> createTimingFormatterText(
23+
llvm::raw_ostream &os);
24+
25+
} // namespace Fortran::support
26+
27+
#endif // FORTRAN_SUPPORT_TIMING_H

flang/lib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ add_subdirectory(Decimal)
44
add_subdirectory(Lower)
55
add_subdirectory(Parser)
66
add_subdirectory(Semantics)
7+
add_subdirectory(Support)
78
add_subdirectory(Frontend)
89
add_subdirectory(FrontendTool)
910

flang/lib/Frontend/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ add_flang_library(flangFrontend
3030
FortranEvaluate
3131
FortranCommon
3232
FortranLower
33+
FortranSupport
3334
FIRDialect
3435
FIRDialectSupport
3536
FIRSupport

flang/lib/Frontend/CompilerInstance.cpp

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717
#include "flang/Parser/parsing.h"
1818
#include "flang/Parser/provenance.h"
1919
#include "flang/Semantics/semantics.h"
20+
#include "flang/Support/Timing.h"
21+
#include "mlir/Support/RawOstreamExtras.h"
2022
#include "clang/Basic/DiagnosticFrontend.h"
2123
#include "llvm/ADT/StringExtras.h"
2224
#include "llvm/MC/TargetRegistry.h"
25+
#include "llvm/Pass.h"
2326
#include "llvm/Support/Errc.h"
2427
#include "llvm/Support/Error.h"
2528
#include "llvm/Support/FileSystem.h"
@@ -147,7 +150,7 @@ void CompilerInstance::clearOutputFiles(bool eraseFiles) {
147150
}
148151

149152
bool CompilerInstance::executeAction(FrontendAction &act) {
150-
auto &invoc = this->getInvocation();
153+
CompilerInvocation &invoc = this->getInvocation();
151154

152155
llvm::Triple targetTriple{llvm::Triple(invoc.getTargetOpts().triple)};
153156
if (targetTriple.getArch() == llvm::Triple::ArchType::x86_64) {
@@ -167,6 +170,32 @@ bool CompilerInstance::executeAction(FrontendAction &act) {
167170
// Set options controlling lowering to FIR.
168171
invoc.setLoweringOptions();
169172

173+
if (invoc.getEnableTimers()) {
174+
// FIXME: Currently, enabling these results in a duplicate registration
175+
// error of the "sort-timers" command line option. It is not clear why that
176+
// is occurring. Without setting these, we cannot get detailed information
177+
// about the runtime of the LLVM IR optimization and code generation passes.
178+
// Once the root cause of this is determined, we should enable this to have
179+
// behavior that is comparable to clang.
180+
// llvm::TimePassesIsEnabled = true;
181+
// llvm::TimePassesPerRun = invoc.getTimeLLVMPassesPerRun();
182+
183+
timingStreamMLIR = std::make_unique<Fortran::support::string_ostream>();
184+
timingStreamLLVM = std::make_unique<Fortran::support::string_ostream>();
185+
timingStreamCodeGen = std::make_unique<Fortran::support::string_ostream>();
186+
187+
timingMgr.setEnabled(true);
188+
timingMgr.setDisplayMode(mlir::DefaultTimingManager::DisplayMode::Tree);
189+
timingMgr.setOutput(
190+
Fortran::support::createTimingFormatterText(*timingStreamMLIR));
191+
192+
// Creating a new TimingScope will automatically start the timer. Since this
193+
// is the top-level timer, this is ok because it will end up capturing the
194+
// time for all the bookkeeping and other tasks that take place between
195+
// parsing, lowering etc. for which finer-grained timers will be created.
196+
timingScopeRoot = timingMgr.getRootScope();
197+
}
198+
170199
// Run the frontend action `act` for every input file.
171200
for (const FrontendInputFile &fif : getFrontendOpts().inputs) {
172201
if (act.beginSourceFile(*this, fif)) {
@@ -176,6 +205,26 @@ bool CompilerInstance::executeAction(FrontendAction &act) {
176205
act.endSourceFile();
177206
}
178207
}
208+
209+
if (timingMgr.isEnabled()) {
210+
timingScopeRoot.stop();
211+
212+
// Write the timings to the associated output stream and clear all timers.
213+
// We need to provide another stream because the TimingManager will attempt
214+
// to print in its destructor even if it has been cleared. By the time that
215+
// destructor runs, the output streams will have been destroyed, so give it
216+
// a null stream.
217+
timingMgr.print();
218+
timingMgr.setOutput(
219+
Fortran::support::createTimingFormatterText(mlir::thread_safe_nulls()));
220+
221+
// This is deliberately done in "reverse" order and does not match the
222+
// behavior of clang.
223+
llvm::errs() << timingStreamCodeGen->str() << "\n";
224+
llvm::errs() << timingStreamLLVM->str() << "\n";
225+
llvm::errs() << timingStreamMLIR->str() << "\n";
226+
}
227+
179228
return !getDiagnostics().getClient()->getNumErrors();
180229
}
181230

flang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,6 +1358,24 @@ bool CompilerInvocation::createFromArgs(
13581358
}
13591359
}
13601360

1361+
// Process the timing-related options.
1362+
if (const llvm::opt::Arg *a =
1363+
args.getLastArg(clang::driver::options::OPT_ftime_report,
1364+
clang::driver::options::OPT_ftime_report_EQ)) {
1365+
invoc.enableTimers = true;
1366+
if (a->getOption().getID() == clang::driver::options::OPT_ftime_report_EQ) {
1367+
llvm::StringRef val = a->getValue();
1368+
if (val == "per-pass") {
1369+
invoc.timeLLVMPassesPerRun = false;
1370+
} else if (val == "per-pass-run") {
1371+
invoc.timeLLVMPassesPerRun = true;
1372+
} else {
1373+
diags.Report(clang::diag::err_drv_invalid_value)
1374+
<< a->getAsString(args) << a->getValue();
1375+
}
1376+
}
1377+
}
1378+
13611379
invoc.setArgv0(argv0);
13621380

13631381
return success;

0 commit comments

Comments
 (0)