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
149152bool 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
0 commit comments