xref: /llvm-project/flang/include/flang/Frontend/CompilerInstance.h (revision 310c281b020b169e760ca75f878f5873ffbb2a9f)
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_FRONTEND_COMPILERINSTANCE_H
14 #define FORTRAN_FRONTEND_COMPILERINSTANCE_H
15 
16 #include "flang/Frontend/CompilerInvocation.h"
17 #include "flang/Frontend/FrontendAction.h"
18 #include "flang/Frontend/PreprocessorOptions.h"
19 #include "flang/Parser/parsing.h"
20 #include "flang/Parser/provenance.h"
21 #include "flang/Semantics/runtime-type-info.h"
22 #include "flang/Semantics/semantics.h"
23 #include "flang/Support/StringOstream.h"
24 #include "llvm/Support/raw_ostream.h"
25 #include "llvm/Target/TargetMachine.h"
26 
27 namespace Fortran::frontend {
28 
29 /// Helper class for managing a single instance of the Flang compiler.
30 ///
31 /// This class serves two purposes:
32 ///  (1) It manages the various objects which are necessary to run the compiler
33 ///  (2) It provides utility routines for constructing and manipulating the
34 ///      common Flang objects.
35 ///
36 /// The compiler instance generally owns the instance of all the objects that it
37 /// manages. However, clients can still share objects by manually setting the
38 /// object and retaking ownership prior to destroying the CompilerInstance.
39 ///
40 /// The compiler instance is intended to simplify clients, but not to lock them
41 /// in to the compiler instance for everything. When possible, utility functions
42 /// come in two forms; a short form that reuses the CompilerInstance objects,
43 /// and a long form that takes explicit instances of any required objects.
44 class CompilerInstance {
45 
46   /// The options used in this compiler instance.
47   std::shared_ptr<CompilerInvocation> invocation;
48 
49   /// Flang file  manager.
50   std::shared_ptr<Fortran::parser::AllSources> allSources;
51 
52   std::shared_ptr<Fortran::parser::AllCookedSources> allCookedSources;
53 
54   std::shared_ptr<Fortran::parser::Parsing> parsing;
55 
56   std::unique_ptr<Fortran::semantics::Semantics> semantics;
57 
58   std::unique_ptr<Fortran::semantics::RuntimeDerivedTypeTables> rtTyTables;
59 
60   std::unique_ptr<Fortran::semantics::SemanticsContext> semaContext;
61 
62   std::unique_ptr<llvm::TargetMachine> targetMachine;
63 
64   /// The stream for diagnostics from Semantics
65   llvm::raw_ostream *semaOutputStream = &llvm::errs();
66 
67   /// The stream for diagnostics from Semantics if owned, otherwise nullptr.
68   std::unique_ptr<llvm::raw_ostream> ownedSemaOutputStream;
69 
70   /// The diagnostics engine instance.
71   llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics;
72 
73   /// Holds information about the output file.
74   struct OutputFile {
75     std::string filename;
76     OutputFile(std::string inputFilename)
77         : filename(std::move(inputFilename)) {}
78   };
79 
80   /// The list of active output files.
81   std::list<OutputFile> outputFiles;
82 
83   /// Holds the output stream provided by the user. Normally, users of
84   /// CompilerInstance will call CreateOutputFile to obtain/create an output
85   /// stream. If they want to provide their own output stream, this field will
86   /// facilitate this. It is optional and will normally be just a nullptr.
87   std::unique_ptr<llvm::raw_pwrite_stream> outputStream;
88 
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 
110 public:
111   explicit CompilerInstance();
112 
113   ~CompilerInstance();
114 
115   /// @name Compiler Invocation
116   /// {
117 
118   CompilerInvocation &getInvocation() {
119     assert(invocation && "Compiler instance has no invocation!");
120     return *invocation;
121   };
122 
123   /// Replace the current invocation.
124   void setInvocation(std::shared_ptr<CompilerInvocation> value);
125 
126   /// }
127   /// @name File manager
128   /// {
129 
130   /// Return the current allSources.
131   Fortran::parser::AllSources &getAllSources() const { return *allSources; }
132 
133   bool hasAllSources() const { return allSources != nullptr; }
134 
135   parser::AllCookedSources &getAllCookedSources() {
136     assert(allCookedSources && "Compiler instance has no AllCookedSources!");
137     return *allCookedSources;
138   };
139 
140   /// }
141   /// @name Parser Operations
142   /// {
143 
144   /// Return parsing to be used by Actions.
145   Fortran::parser::Parsing &getParsing() const { return *parsing; }
146 
147   /// }
148   /// @name Semantic analysis
149   /// {
150 
151   Fortran::semantics::SemanticsContext &getSemanticsContext() {
152     return *semaContext;
153   }
154   const Fortran::semantics::SemanticsContext &getSemanticsContext() const {
155     return *semaContext;
156   }
157 
158   /// Replace the current stream for verbose output.
159   void setSemaOutputStream(llvm::raw_ostream &value);
160 
161   /// Replace the current stream for verbose output.
162   void setSemaOutputStream(std::unique_ptr<llvm::raw_ostream> value);
163 
164   /// Get the current stream for verbose output.
165   llvm::raw_ostream &getSemaOutputStream() { return *semaOutputStream; }
166 
167   Fortran::semantics::Semantics &getSemantics() { return *semantics; }
168   const Fortran::semantics::Semantics &getSemantics() const {
169     return *semantics;
170   }
171 
172   void setSemantics(std::unique_ptr<Fortran::semantics::Semantics> sema) {
173     semantics = std::move(sema);
174   }
175 
176   void setRtTyTables(
177       std::unique_ptr<Fortran::semantics::RuntimeDerivedTypeTables> tables) {
178     rtTyTables = std::move(tables);
179   }
180 
181   Fortran::semantics::RuntimeDerivedTypeTables &getRtTyTables() {
182     assert(rtTyTables && "Missing runtime derived type tables!");
183     return *rtTyTables;
184   }
185 
186   /// }
187   /// @name High-Level Operations
188   /// {
189 
190   /// Execute the provided action against the compiler's
191   /// CompilerInvocation object.
192   /// \param act - The action to execute.
193   /// \return - True on success.
194   bool executeAction(FrontendAction &act);
195 
196   /// }
197   /// @name Forwarding Methods
198   /// {
199 
200   clang::DiagnosticOptions &getDiagnosticOpts() {
201     return invocation->getDiagnosticOpts();
202   }
203   const clang::DiagnosticOptions &getDiagnosticOpts() const {
204     return invocation->getDiagnosticOpts();
205   }
206 
207   FrontendOptions &getFrontendOpts() { return invocation->getFrontendOpts(); }
208   const FrontendOptions &getFrontendOpts() const {
209     return invocation->getFrontendOpts();
210   }
211 
212   PreprocessorOptions &preprocessorOpts() {
213     return invocation->getPreprocessorOpts();
214   }
215   const PreprocessorOptions &preprocessorOpts() const {
216     return invocation->getPreprocessorOpts();
217   }
218 
219   /// }
220   /// @name Diagnostics Engine
221   /// {
222 
223   bool hasDiagnostics() const { return diagnostics != nullptr; }
224 
225   /// Get the current diagnostics engine.
226   clang::DiagnosticsEngine &getDiagnostics() const {
227     assert(diagnostics && "Compiler instance has no diagnostics!");
228     return *diagnostics;
229   }
230 
231   clang::DiagnosticConsumer &getDiagnosticClient() const {
232     assert(diagnostics && diagnostics->getClient() &&
233            "Compiler instance has no diagnostic client!");
234     return *diagnostics->getClient();
235   }
236 
237   /// {
238   /// @name Output Files
239   /// {
240 
241   /// Clear the output file list.
242   void clearOutputFiles(bool eraseFiles);
243 
244   /// Create the default output file (based on the invocation's options) and
245   /// add it to the list of tracked output files. If the name of the output
246   /// file is not provided, it will be derived from the input file.
247   ///
248   /// \param binary     The mode to open the file in.
249   /// \param baseInput  If the invocation contains no output file name (i.e.
250   ///                   outputFile in FrontendOptions is empty), the input path
251   ///                   name to use for deriving the output path.
252   /// \param extension  The extension to use for output names derived from
253   ///                   \p baseInput.
254   /// \return           Null on error, ostream for the output file otherwise
255   std::unique_ptr<llvm::raw_pwrite_stream>
256   createDefaultOutputFile(bool binary = true, llvm::StringRef baseInput = "",
257                           llvm::StringRef extension = "");
258 
259   /// {
260   /// @name Target Machine
261   /// {
262 
263   /// Get the target machine.
264   const llvm::TargetMachine &getTargetMachine() const {
265     assert(targetMachine && "target machine was not set");
266     return *targetMachine;
267   }
268   llvm::TargetMachine &getTargetMachine() {
269     assert(targetMachine && "target machine was not set");
270     return *targetMachine;
271   }
272 
273   /// Sets up LLVM's TargetMachine.
274   bool setUpTargetMachine();
275 
276   /// Produces the string which represents target feature
277   std::string getTargetFeatures();
278 
279   /// {
280   /// @name Timing
281   /// @{
282   bool isTimingEnabled() const { return timingMgr.isEnabled(); }
283 
284   mlir::DefaultTimingManager &getTimingManager() { return timingMgr; }
285   const mlir::DefaultTimingManager &getTimingManager() const {
286     return timingMgr;
287   }
288 
289   mlir::TimingScope &getTimingScopeRoot() { return timingScopeRoot; }
290   const mlir::TimingScope &getTimingScopeRoot() const {
291     return timingScopeRoot;
292   }
293 
294   /// Get the timing stream for the MLIR pass manager.
295   llvm::raw_ostream &getTimingStreamMLIR() {
296     assert(timingStreamMLIR && "Timing stream for MLIR was not set");
297     return *timingStreamMLIR;
298   }
299 
300   /// Get the timing stream for the new LLVM pass manager.
301   llvm::raw_ostream &getTimingStreamLLVM() {
302     assert(timingStreamLLVM && "Timing stream for LLVM was not set");
303     return *timingStreamLLVM;
304   }
305 
306   /// Get the timing stream fro the legacy LLVM pass manager.
307   /// NOTE: If the codegen is updated to use the new pass manager, this should
308   /// no longer be needed.
309   llvm::raw_ostream &getTimingStreamCodeGen() {
310     assert(timingStreamCodeGen && "Timing stream for codegen was not set");
311     return *timingStreamCodeGen;
312   }
313   /// @}
314 
315 private:
316   /// Create a new output file
317   ///
318   /// \param outputPath   The path to the output file.
319   /// \param binary       The mode to open the file in.
320   /// \return             Null on error, ostream for the output file otherwise
321   llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>>
322   createOutputFileImpl(llvm::StringRef outputPath, bool binary);
323 
324 public:
325   /// }
326   /// @name Construction Utility Methods
327   /// {
328 
329   /// Create a DiagnosticsEngine object
330   ///
331   /// If no diagnostic client is provided, this method creates a
332   /// DiagnosticConsumer that is owned by the returned diagnostic object. If
333   /// using directly the caller is responsible for releasing the returned
334   /// DiagnosticsEngine's client eventually.
335   ///
336   /// \param opts - The diagnostic options; note that the created text
337   /// diagnostic object contains a reference to these options.
338   ///
339   /// \param client - If non-NULL, a diagnostic client that will be attached to
340   /// (and optionally, depending on /p shouldOwnClient, owned by) the returned
341   /// DiagnosticsEngine object.
342   ///
343   /// \return The new object on success, or null on failure.
344   static clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine>
345   createDiagnostics(clang::DiagnosticOptions *opts,
346                     clang::DiagnosticConsumer *client = nullptr,
347                     bool shouldOwnClient = true);
348   void createDiagnostics(clang::DiagnosticConsumer *client = nullptr,
349                          bool shouldOwnClient = true);
350 
351   /// }
352   /// @name Output Stream Methods
353   /// {
354   void setOutputStream(std::unique_ptr<llvm::raw_pwrite_stream> outStream) {
355     outputStream = std::move(outStream);
356   }
357 
358   bool isOutputStreamNull() { return (outputStream == nullptr); }
359 
360   // Allow the frontend compiler to write in the output stream.
361   void writeOutputStream(const std::string &message) {
362     *outputStream << message;
363   }
364 
365   /// Get the user specified output stream.
366   llvm::raw_pwrite_stream &getOutputStream() {
367     assert(outputStream &&
368            "Compiler instance has no user-specified output stream!");
369     return *outputStream;
370   }
371 };
372 
373 } // end namespace Fortran::frontend
374 #endif // FORTRAN_FRONTEND_COMPILERINSTANCE_H
375