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