1 //===--- CompilerInstance.cpp ---------------------------------------------===// 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 #include "flang/Frontend/CompilerInstance.h" 14 #include "flang/Common/Fortran-features.h" 15 #include "flang/Frontend/CompilerInvocation.h" 16 #include "flang/Frontend/TextDiagnosticPrinter.h" 17 #include "flang/Parser/parsing.h" 18 #include "flang/Parser/provenance.h" 19 #include "flang/Semantics/semantics.h" 20 #include "llvm/ADT/Triple.h" 21 #include "llvm/Support/Errc.h" 22 #include "llvm/Support/Error.h" 23 #include "llvm/Support/FileSystem.h" 24 #include "llvm/Support/Path.h" 25 #include "llvm/Support/raw_ostream.h" 26 27 using namespace Fortran::frontend; 28 29 CompilerInstance::CompilerInstance() 30 : invocation(new CompilerInvocation()), 31 allSources(new Fortran::parser::AllSources()), 32 allCookedSources(new Fortran::parser::AllCookedSources(*allSources)), 33 parsing(new Fortran::parser::Parsing(*allCookedSources)) { 34 // TODO: This is a good default during development, but ultimately we should 35 // give the user the opportunity to specify this. 36 allSources->set_encoding(Fortran::parser::Encoding::UTF_8); 37 } 38 39 CompilerInstance::~CompilerInstance() { 40 assert(outputFiles.empty() && "Still output files in flight?"); 41 } 42 43 void CompilerInstance::setInvocation( 44 std::shared_ptr<CompilerInvocation> value) { 45 invocation = std::move(value); 46 } 47 48 void CompilerInstance::setSemaOutputStream(raw_ostream &value) { 49 ownedSemaOutputStream.release(); 50 semaOutputStream = &value; 51 } 52 53 void CompilerInstance::setSemaOutputStream(std::unique_ptr<raw_ostream> value) { 54 ownedSemaOutputStream.swap(value); 55 semaOutputStream = ownedSemaOutputStream.get(); 56 } 57 58 // Helper method to generate the path of the output file. The following logic 59 // applies: 60 // 1. If the user specifies the output file via `-o`, then use that (i.e. 61 // the outputFilename parameter). 62 // 2. If the user does not specify the name of the output file, derive it from 63 // the input file (i.e. inputFilename + extension) 64 // 3. If the output file is not specified and the input file is `-`, then set 65 // the output file to `-` as well. 66 static std::string getOutputFilePath(llvm::StringRef outputFilename, 67 llvm::StringRef inputFilename, 68 llvm::StringRef extension) { 69 70 // Output filename _is_ specified. Just use that. 71 if (!outputFilename.empty()) 72 return std::string(outputFilename); 73 74 // Output filename _is not_ specified. Derive it from the input file name. 75 std::string outFile = "-"; 76 if (!extension.empty() && (inputFilename != "-")) { 77 llvm::SmallString<128> path(inputFilename); 78 llvm::sys::path::replace_extension(path, extension); 79 outFile = std::string(path.str()); 80 } 81 82 return outFile; 83 } 84 85 std::unique_ptr<llvm::raw_pwrite_stream> 86 CompilerInstance::createDefaultOutputFile(bool binary, llvm::StringRef baseName, 87 llvm::StringRef extension) { 88 89 // Get the path of the output file 90 std::string outputFilePath = 91 getOutputFilePath(getFrontendOpts().outputFile, baseName, extension); 92 93 // Create the output file 94 llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>> os = 95 createOutputFileImpl(outputFilePath, binary); 96 97 // If successful, add the file to the list of tracked output files and 98 // return. 99 if (os) { 100 outputFiles.emplace_back(OutputFile(outputFilePath)); 101 return std::move(*os); 102 } 103 104 // If unsuccessful, issue an error and return Null 105 unsigned diagID = getDiagnostics().getCustomDiagID( 106 clang::DiagnosticsEngine::Error, "unable to open output file '%0': '%1'"); 107 getDiagnostics().Report(diagID) 108 << outputFilePath << llvm::errorToErrorCode(os.takeError()).message(); 109 return nullptr; 110 } 111 112 llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>> 113 CompilerInstance::createOutputFileImpl(llvm::StringRef outputFilePath, 114 bool binary) { 115 116 // Creates the file descriptor for the output file 117 std::unique_ptr<llvm::raw_fd_ostream> os; 118 119 std::error_code error; 120 os.reset(new llvm::raw_fd_ostream(outputFilePath, error, 121 (binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF))); 122 if (error) { 123 return llvm::errorCodeToError(error); 124 } 125 126 // For seekable streams, just return the stream corresponding to the output 127 // file. 128 if (!binary || os->supportsSeeking()) 129 return std::move(os); 130 131 // For non-seekable streams, we need to wrap the output stream into something 132 // that supports 'pwrite' and takes care of the ownership for us. 133 return std::make_unique<llvm::buffer_unique_ostream>(std::move(os)); 134 } 135 136 void CompilerInstance::clearOutputFiles(bool eraseFiles) { 137 for (OutputFile &of : outputFiles) 138 if (!of.filename.empty() && eraseFiles) 139 llvm::sys::fs::remove(of.filename); 140 141 outputFiles.clear(); 142 } 143 144 bool CompilerInstance::executeAction(FrontendAction &act) { 145 auto &invoc = this->getInvocation(); 146 147 llvm::Triple targetTriple{llvm::Triple(invoc.getTargetOpts().triple)}; 148 if (targetTriple.getArch() == llvm::Triple::ArchType::x86_64) { 149 invoc.getDefaultKinds().set_quadPrecisionKind(10); 150 } 151 152 // Set some sane defaults for the frontend. 153 invoc.setDefaultFortranOpts(); 154 // Update the fortran options based on user-based input. 155 invoc.setFortranOpts(); 156 // Set the encoding to read all input files in based on user input. 157 allSources->set_encoding(invoc.getFortranOpts().encoding); 158 // Create the semantics context and set semantic options. 159 invoc.setSemanticsOpts(*this->allCookedSources); 160 // Set options controlling lowering to FIR. 161 invoc.setLoweringOptions(); 162 163 // Run the frontend action `act` for every input file. 164 for (const FrontendInputFile &fif : getFrontendOpts().inputs) { 165 if (act.beginSourceFile(*this, fif)) { 166 if (llvm::Error err = act.execute()) { 167 consumeError(std::move(err)); 168 } 169 act.endSourceFile(); 170 } 171 } 172 return !getDiagnostics().getClient()->getNumErrors(); 173 } 174 175 void CompilerInstance::createDiagnostics(clang::DiagnosticConsumer *client, 176 bool shouldOwnClient) { 177 diagnostics = 178 createDiagnostics(&getDiagnosticOpts(), client, shouldOwnClient); 179 } 180 181 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> 182 CompilerInstance::createDiagnostics(clang::DiagnosticOptions *opts, 183 clang::DiagnosticConsumer *client, 184 bool shouldOwnClient) { 185 clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID( 186 new clang::DiagnosticIDs()); 187 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags( 188 new clang::DiagnosticsEngine(diagID, opts)); 189 190 // Create the diagnostic client for reporting errors or for 191 // implementing -verify. 192 if (client) { 193 diags->setClient(client, shouldOwnClient); 194 } else { 195 diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts)); 196 } 197 return diags; 198 } 199