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/Support/Errc.h" 21 #include "llvm/Support/Error.h" 22 #include "llvm/Support/FileSystem.h" 23 #include "llvm/Support/Path.h" 24 #include "llvm/Support/raw_ostream.h" 25 #include "llvm/TargetParser/Triple.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( 121 outputFilePath, error, 122 (binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF))); 123 if (error) { 124 return llvm::errorCodeToError(error); 125 } 126 127 // For seekable streams, just return the stream corresponding to the output 128 // file. 129 if (!binary || os->supportsSeeking()) 130 return std::move(os); 131 132 // For non-seekable streams, we need to wrap the output stream into something 133 // that supports 'pwrite' and takes care of the ownership for us. 134 return std::make_unique<llvm::buffer_unique_ostream>(std::move(os)); 135 } 136 137 void CompilerInstance::clearOutputFiles(bool eraseFiles) { 138 for (OutputFile &of : outputFiles) 139 if (!of.filename.empty() && eraseFiles) 140 llvm::sys::fs::remove(of.filename); 141 142 outputFiles.clear(); 143 } 144 145 bool CompilerInstance::executeAction(FrontendAction &act) { 146 auto &invoc = this->getInvocation(); 147 148 llvm::Triple targetTriple{llvm::Triple(invoc.getTargetOpts().triple)}; 149 if (targetTriple.getArch() == llvm::Triple::ArchType::x86_64) { 150 invoc.getDefaultKinds().set_quadPrecisionKind(10); 151 } 152 153 // Set some sane defaults for the frontend. 154 invoc.setDefaultFortranOpts(); 155 // Update the fortran options based on user-based input. 156 invoc.setFortranOpts(); 157 // Set the encoding to read all input files in based on user input. 158 allSources->set_encoding(invoc.getFortranOpts().encoding); 159 // Create the semantics context 160 semaContext = invoc.getSemanticsCtx(*allCookedSources); 161 // Set options controlling lowering to FIR. 162 invoc.setLoweringOptions(); 163 164 // Run the frontend action `act` for every input file. 165 for (const FrontendInputFile &fif : getFrontendOpts().inputs) { 166 if (act.beginSourceFile(*this, fif)) { 167 if (llvm::Error err = act.execute()) { 168 consumeError(std::move(err)); 169 } 170 act.endSourceFile(); 171 } 172 } 173 return !getDiagnostics().getClient()->getNumErrors(); 174 } 175 176 void CompilerInstance::createDiagnostics(clang::DiagnosticConsumer *client, 177 bool shouldOwnClient) { 178 diagnostics = 179 createDiagnostics(&getDiagnosticOpts(), client, shouldOwnClient); 180 } 181 182 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> 183 CompilerInstance::createDiagnostics(clang::DiagnosticOptions *opts, 184 clang::DiagnosticConsumer *client, 185 bool shouldOwnClient) { 186 clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID( 187 new clang::DiagnosticIDs()); 188 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags( 189 new clang::DiagnosticsEngine(diagID, opts)); 190 191 // Create the diagnostic client for reporting errors or for 192 // implementing -verify. 193 if (client) { 194 diags->setClient(client, shouldOwnClient); 195 } else { 196 diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts)); 197 } 198 return diags; 199 } 200