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 "llvm/Support/raw_ostream.h" 24 25 namespace Fortran::frontend { 26 27 /// Helper class for managing a single instance of the Flang compiler. 28 /// 29 /// This class serves two purposes: 30 /// (1) It manages the various objects which are necessary to run the compiler 31 /// (2) It provides utility routines for constructing and manipulating the 32 /// common Flang objects. 33 /// 34 /// The compiler instance generally owns the instance of all the objects that it 35 /// manages. However, clients can still share objects by manually setting the 36 /// object and retaking ownership prior to destroying the CompilerInstance. 37 /// 38 /// The compiler instance is intended to simplify clients, but not to lock them 39 /// in to the compiler instance for everything. When possible, utility functions 40 /// come in two forms; a short form that reuses the CompilerInstance objects, 41 /// and a long form that takes explicit instances of any required objects. 42 class CompilerInstance { 43 44 /// The options used in this compiler instance. 45 std::shared_ptr<CompilerInvocation> invocation; 46 47 /// Flang file manager. 48 std::shared_ptr<Fortran::parser::AllSources> allSources; 49 50 std::shared_ptr<Fortran::parser::AllCookedSources> allCookedSources; 51 52 std::shared_ptr<Fortran::parser::Parsing> parsing; 53 54 std::unique_ptr<Fortran::semantics::Semantics> semantics; 55 56 std::unique_ptr<Fortran::semantics::RuntimeDerivedTypeTables> rtTyTables; 57 58 /// The stream for diagnostics from Semantics 59 llvm::raw_ostream *semaOutputStream = &llvm::errs(); 60 61 /// The stream for diagnostics from Semantics if owned, otherwise nullptr. 62 std::unique_ptr<llvm::raw_ostream> ownedSemaOutputStream; 63 64 /// The diagnostics engine instance. 65 llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnostics; 66 67 /// Holds information about the output file. 68 struct OutputFile { 69 std::string filename; OutputFileOutputFile70 OutputFile(std::string inputFilename) 71 : filename(std::move(inputFilename)) {} 72 }; 73 74 /// The list of active output files. 75 std::list<OutputFile> outputFiles; 76 77 /// Holds the output stream provided by the user. Normally, users of 78 /// CompilerInstance will call CreateOutputFile to obtain/create an output 79 /// stream. If they want to provide their own output stream, this field will 80 /// facilitate this. It is optional and will normally be just a nullptr. 81 std::unique_ptr<llvm::raw_pwrite_stream> outputStream; 82 83 public: 84 explicit CompilerInstance(); 85 86 ~CompilerInstance(); 87 88 /// @name Compiler Invocation 89 /// { 90 getInvocation()91 CompilerInvocation &getInvocation() { 92 assert(invocation && "Compiler instance has no invocation!"); 93 return *invocation; 94 }; 95 96 /// Replace the current invocation. 97 void setInvocation(std::shared_ptr<CompilerInvocation> value); 98 99 /// } 100 /// @name File manager 101 /// { 102 103 /// Return the current allSources. getAllSources()104 Fortran::parser::AllSources &getAllSources() const { return *allSources; } 105 hasAllSources()106 bool hasAllSources() const { return allSources != nullptr; } 107 getAllCookedSources()108 parser::AllCookedSources &getAllCookedSources() { 109 assert(allCookedSources && "Compiler instance has no AllCookedSources!"); 110 return *allCookedSources; 111 }; 112 113 /// } 114 /// @name Parser Operations 115 /// { 116 117 /// Return parsing to be used by Actions. getParsing()118 Fortran::parser::Parsing &getParsing() const { return *parsing; } 119 120 /// } 121 /// @name Semantic analysis 122 /// { 123 124 /// Replace the current stream for verbose output. 125 void setSemaOutputStream(llvm::raw_ostream &value); 126 127 /// Replace the current stream for verbose output. 128 void setSemaOutputStream(std::unique_ptr<llvm::raw_ostream> value); 129 130 /// Get the current stream for verbose output. getSemaOutputStream()131 llvm::raw_ostream &getSemaOutputStream() { return *semaOutputStream; } 132 getSemantics()133 Fortran::semantics::Semantics &getSemantics() { return *semantics; } getSemantics()134 const Fortran::semantics::Semantics &getSemantics() const { 135 return *semantics; 136 } 137 setSemantics(std::unique_ptr<Fortran::semantics::Semantics> sema)138 void setSemantics(std::unique_ptr<Fortran::semantics::Semantics> sema) { 139 semantics = std::move(sema); 140 } 141 setRtTyTables(std::unique_ptr<Fortran::semantics::RuntimeDerivedTypeTables> tables)142 void setRtTyTables( 143 std::unique_ptr<Fortran::semantics::RuntimeDerivedTypeTables> tables) { 144 rtTyTables = std::move(tables); 145 } 146 getRtTyTables()147 Fortran::semantics::RuntimeDerivedTypeTables &getRtTyTables() { 148 assert(rtTyTables && "Missing runtime derived type tables!"); 149 return *rtTyTables; 150 } 151 152 /// } 153 /// @name High-Level Operations 154 /// { 155 156 /// Execute the provided action against the compiler's 157 /// CompilerInvocation object. 158 /// \param act - The action to execute. 159 /// \return - True on success. 160 bool executeAction(FrontendAction &act); 161 162 /// } 163 /// @name Forwarding Methods 164 /// { 165 getDiagnosticOpts()166 clang::DiagnosticOptions &getDiagnosticOpts() { 167 return invocation->getDiagnosticOpts(); 168 } getDiagnosticOpts()169 const clang::DiagnosticOptions &getDiagnosticOpts() const { 170 return invocation->getDiagnosticOpts(); 171 } 172 getFrontendOpts()173 FrontendOptions &getFrontendOpts() { return invocation->getFrontendOpts(); } getFrontendOpts()174 const FrontendOptions &getFrontendOpts() const { 175 return invocation->getFrontendOpts(); 176 } 177 preprocessorOpts()178 PreprocessorOptions &preprocessorOpts() { 179 return invocation->getPreprocessorOpts(); 180 } preprocessorOpts()181 const PreprocessorOptions &preprocessorOpts() const { 182 return invocation->getPreprocessorOpts(); 183 } 184 185 /// } 186 /// @name Diagnostics Engine 187 /// { 188 hasDiagnostics()189 bool hasDiagnostics() const { return diagnostics != nullptr; } 190 191 /// Get the current diagnostics engine. getDiagnostics()192 clang::DiagnosticsEngine &getDiagnostics() const { 193 assert(diagnostics && "Compiler instance has no diagnostics!"); 194 return *diagnostics; 195 } 196 getDiagnosticClient()197 clang::DiagnosticConsumer &getDiagnosticClient() const { 198 assert(diagnostics && diagnostics->getClient() && 199 "Compiler instance has no diagnostic client!"); 200 return *diagnostics->getClient(); 201 } 202 203 /// { 204 /// @name Output Files 205 /// { 206 207 /// Clear the output file list. 208 void clearOutputFiles(bool eraseFiles); 209 210 /// Create the default output file (based on the invocation's options) and 211 /// add it to the list of tracked output files. If the name of the output 212 /// file is not provided, it will be derived from the input file. 213 /// 214 /// \param binary The mode to open the file in. 215 /// \param baseInput If the invocation contains no output file name (i.e. 216 /// outputFile in FrontendOptions is empty), the input path 217 /// name to use for deriving the output path. 218 /// \param extension The extension to use for output names derived from 219 /// \p baseInput. 220 /// \return Null on error, ostream for the output file otherwise 221 std::unique_ptr<llvm::raw_pwrite_stream> 222 createDefaultOutputFile(bool binary = true, llvm::StringRef baseInput = "", 223 llvm::StringRef extension = ""); 224 225 private: 226 /// Create a new output file 227 /// 228 /// \param outputPath The path to the output file. 229 /// \param binary The mode to open the file in. 230 /// \return Null on error, ostream for the output file otherwise 231 llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>> 232 createOutputFileImpl(llvm::StringRef outputPath, bool binary); 233 234 public: 235 /// } 236 /// @name Construction Utility Methods 237 /// { 238 239 /// Create a DiagnosticsEngine object 240 /// 241 /// If no diagnostic client is provided, this method creates a 242 /// DiagnosticConsumer that is owned by the returned diagnostic object. If 243 /// using directly the caller is responsible for releasing the returned 244 /// DiagnosticsEngine's client eventually. 245 /// 246 /// \param opts - The diagnostic options; note that the created text 247 /// diagnostic object contains a reference to these options. 248 /// 249 /// \param client - If non-NULL, a diagnostic client that will be attached to 250 /// (and optionally, depending on /p shouldOwnClient, owned by) the returned 251 /// DiagnosticsEngine object. 252 /// 253 /// \return The new object on success, or null on failure. 254 static clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> 255 createDiagnostics(clang::DiagnosticOptions *opts, 256 clang::DiagnosticConsumer *client = nullptr, 257 bool shouldOwnClient = true); 258 void createDiagnostics(clang::DiagnosticConsumer *client = nullptr, 259 bool shouldOwnClient = true); 260 261 /// } 262 /// @name Output Stream Methods 263 /// { setOutputStream(std::unique_ptr<llvm::raw_pwrite_stream> outStream)264 void setOutputStream(std::unique_ptr<llvm::raw_pwrite_stream> outStream) { 265 outputStream = std::move(outStream); 266 } 267 isOutputStreamNull()268 bool isOutputStreamNull() { return (outputStream == nullptr); } 269 270 // Allow the frontend compiler to write in the output stream. writeOutputStream(const std::string & message)271 void writeOutputStream(const std::string &message) { 272 *outputStream << message; 273 } 274 275 /// Get the user specified output stream. getOutputStream()276 llvm::raw_pwrite_stream &getOutputStream() { 277 assert(outputStream && 278 "Compiler instance has no user-specified output stream!"); 279 return *outputStream; 280 } 281 }; 282 283 } // end namespace Fortran::frontend 284 #endif // FORTRAN_FRONTEND_COMPILERINSTANCE_H 285