xref: /llvm-project/flang/lib/Frontend/CompilerInstance.cpp (revision 686a951d4b0e006ee2f6e3c0d35ae41b9ee455f9)
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