LAA: improve code in getStrideFromPointer (NFC) (#124780)
[llvm-project.git] / flang / lib / Frontend / CompilerInstance.cpp
blobdfd15b9c3c39d973b65a3816e541ae3ebd344358
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/
11 //===----------------------------------------------------------------------===//
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 "flang/Support/Timing.h"
21 #include "mlir/Support/RawOstreamExtras.h"
22 #include "clang/Basic/DiagnosticFrontend.h"
23 #include "llvm/ADT/StringExtras.h"
24 #include "llvm/MC/TargetRegistry.h"
25 #include "llvm/Pass.h"
26 #include "llvm/Support/Errc.h"
27 #include "llvm/Support/Error.h"
28 #include "llvm/Support/FileSystem.h"
29 #include "llvm/Support/Path.h"
30 #include "llvm/Support/raw_ostream.h"
31 #include "llvm/TargetParser/TargetParser.h"
32 #include "llvm/TargetParser/Triple.h"
34 using namespace Fortran::frontend;
36 CompilerInstance::CompilerInstance()
37 : invocation(new CompilerInvocation()),
38 allSources(new Fortran::parser::AllSources()),
39 allCookedSources(new Fortran::parser::AllCookedSources(*allSources)),
40 parsing(new Fortran::parser::Parsing(*allCookedSources)) {
41 // TODO: This is a good default during development, but ultimately we should
42 // give the user the opportunity to specify this.
43 allSources->set_encoding(Fortran::parser::Encoding::UTF_8);
46 CompilerInstance::~CompilerInstance() {
47 assert(outputFiles.empty() && "Still output files in flight?");
50 void CompilerInstance::setInvocation(
51 std::shared_ptr<CompilerInvocation> value) {
52 invocation = std::move(value);
55 void CompilerInstance::setSemaOutputStream(raw_ostream &value) {
56 ownedSemaOutputStream.release();
57 semaOutputStream = &value;
60 void CompilerInstance::setSemaOutputStream(std::unique_ptr<raw_ostream> value) {
61 ownedSemaOutputStream.swap(value);
62 semaOutputStream = ownedSemaOutputStream.get();
65 // Helper method to generate the path of the output file. The following logic
66 // applies:
67 // 1. If the user specifies the output file via `-o`, then use that (i.e.
68 // the outputFilename parameter).
69 // 2. If the user does not specify the name of the output file, derive it from
70 // the input file (i.e. inputFilename + extension)
71 // 3. If the output file is not specified and the input file is `-`, then set
72 // the output file to `-` as well.
73 static std::string getOutputFilePath(llvm::StringRef outputFilename,
74 llvm::StringRef inputFilename,
75 llvm::StringRef extension) {
77 // Output filename _is_ specified. Just use that.
78 if (!outputFilename.empty())
79 return std::string(outputFilename);
81 // Output filename _is not_ specified. Derive it from the input file name.
82 std::string outFile = "-";
83 if (!extension.empty() && (inputFilename != "-")) {
84 llvm::SmallString<128> path(inputFilename);
85 llvm::sys::path::replace_extension(path, extension);
86 outFile = std::string(path);
89 return outFile;
92 std::unique_ptr<llvm::raw_pwrite_stream>
93 CompilerInstance::createDefaultOutputFile(bool binary, llvm::StringRef baseName,
94 llvm::StringRef extension) {
96 // Get the path of the output file
97 std::string outputFilePath =
98 getOutputFilePath(getFrontendOpts().outputFile, baseName, extension);
100 // Create the output file
101 llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>> os =
102 createOutputFileImpl(outputFilePath, binary);
104 // If successful, add the file to the list of tracked output files and
105 // return.
106 if (os) {
107 outputFiles.emplace_back(OutputFile(outputFilePath));
108 return std::move(*os);
111 // If unsuccessful, issue an error and return Null
112 unsigned diagID = getDiagnostics().getCustomDiagID(
113 clang::DiagnosticsEngine::Error, "unable to open output file '%0': '%1'");
114 getDiagnostics().Report(diagID)
115 << outputFilePath << llvm::errorToErrorCode(os.takeError()).message();
116 return nullptr;
119 llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>>
120 CompilerInstance::createOutputFileImpl(llvm::StringRef outputFilePath,
121 bool binary) {
123 // Creates the file descriptor for the output file
124 std::unique_ptr<llvm::raw_fd_ostream> os;
126 std::error_code error;
127 os.reset(new llvm::raw_fd_ostream(
128 outputFilePath, error,
129 (binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF)));
130 if (error) {
131 return llvm::errorCodeToError(error);
134 // For seekable streams, just return the stream corresponding to the output
135 // file.
136 if (!binary || os->supportsSeeking())
137 return std::move(os);
139 // For non-seekable streams, we need to wrap the output stream into something
140 // that supports 'pwrite' and takes care of the ownership for us.
141 return std::make_unique<llvm::buffer_unique_ostream>(std::move(os));
144 void CompilerInstance::clearOutputFiles(bool eraseFiles) {
145 for (OutputFile &of : outputFiles)
146 if (!of.filename.empty() && eraseFiles)
147 llvm::sys::fs::remove(of.filename);
149 outputFiles.clear();
152 bool CompilerInstance::executeAction(FrontendAction &act) {
153 CompilerInvocation &invoc = this->getInvocation();
155 llvm::Triple targetTriple{llvm::Triple(invoc.getTargetOpts().triple)};
157 // Set some sane defaults for the frontend.
158 invoc.setDefaultFortranOpts();
159 // Update the fortran options based on user-based input.
160 invoc.setFortranOpts();
161 // Set the encoding to read all input files in based on user input.
162 allSources->set_encoding(invoc.getFortranOpts().encoding);
163 if (!setUpTargetMachine())
164 return false;
165 // Create the semantics context
166 semaContext = invoc.getSemanticsCtx(*allCookedSources, getTargetMachine());
167 // Set options controlling lowering to FIR.
168 invoc.setLoweringOptions();
170 if (invoc.getEnableTimers()) {
171 llvm::TimePassesIsEnabled = true;
173 timingStreamMLIR = std::make_unique<Fortran::support::string_ostream>();
174 timingStreamLLVM = std::make_unique<Fortran::support::string_ostream>();
175 timingStreamCodeGen = std::make_unique<Fortran::support::string_ostream>();
177 timingMgr.setEnabled(true);
178 timingMgr.setDisplayMode(mlir::DefaultTimingManager::DisplayMode::Tree);
179 timingMgr.setOutput(
180 Fortran::support::createTimingFormatterText(*timingStreamMLIR));
182 // Creating a new TimingScope will automatically start the timer. Since this
183 // is the top-level timer, this is ok because it will end up capturing the
184 // time for all the bookkeeping and other tasks that take place between
185 // parsing, lowering etc. for which finer-grained timers will be created.
186 timingScopeRoot = timingMgr.getRootScope();
189 // Run the frontend action `act` for every input file.
190 for (const FrontendInputFile &fif : getFrontendOpts().inputs) {
191 if (act.beginSourceFile(*this, fif)) {
192 if (llvm::Error err = act.execute()) {
193 consumeError(std::move(err));
195 act.endSourceFile();
199 if (timingMgr.isEnabled()) {
200 timingScopeRoot.stop();
202 // Write the timings to the associated output stream and clear all timers.
203 // We need to provide another stream because the TimingManager will attempt
204 // to print in its destructor even if it has been cleared. By the time that
205 // destructor runs, the output streams will have been destroyed, so give it
206 // a null stream.
207 timingMgr.print();
208 timingMgr.setOutput(
209 Fortran::support::createTimingFormatterText(mlir::thread_safe_nulls()));
211 // This prints the timings in "reverse" order, starting from code
212 // generation, followed by LLVM-IR optimizations, then MLIR optimizations
213 // and transformations and the frontend. If any of the steps are disabled,
214 // for instance because code generation was not performed, the strings
215 // will be empty.
216 if (!timingStreamCodeGen->str().empty())
217 llvm::errs() << timingStreamCodeGen->str() << "\n";
219 if (!timingStreamLLVM->str().empty())
220 llvm::errs() << timingStreamLLVM->str() << "\n";
222 if (!timingStreamMLIR->str().empty())
223 llvm::errs() << timingStreamMLIR->str() << "\n";
226 return !getDiagnostics().getClient()->getNumErrors();
229 void CompilerInstance::createDiagnostics(clang::DiagnosticConsumer *client,
230 bool shouldOwnClient) {
231 diagnostics =
232 createDiagnostics(&getDiagnosticOpts(), client, shouldOwnClient);
235 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine>
236 CompilerInstance::createDiagnostics(clang::DiagnosticOptions *opts,
237 clang::DiagnosticConsumer *client,
238 bool shouldOwnClient) {
239 clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
240 new clang::DiagnosticIDs());
241 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
242 new clang::DiagnosticsEngine(diagID, opts));
244 // Create the diagnostic client for reporting errors or for
245 // implementing -verify.
246 if (client) {
247 diags->setClient(client, shouldOwnClient);
248 } else {
249 diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts));
251 return diags;
254 // Get feature string which represents combined explicit target features
255 // for AMD GPU and the target features specified by the user
256 static std::string
257 getExplicitAndImplicitAMDGPUTargetFeatures(clang::DiagnosticsEngine &diags,
258 const TargetOptions &targetOpts,
259 const llvm::Triple triple) {
260 llvm::StringRef cpu = targetOpts.cpu;
261 llvm::StringMap<bool> implicitFeaturesMap;
262 // Get the set of implicit target features
263 llvm::AMDGPU::fillAMDGPUFeatureMap(cpu, triple, implicitFeaturesMap);
265 // Add target features specified by the user
266 for (auto &userFeature : targetOpts.featuresAsWritten) {
267 std::string userKeyString = userFeature.substr(1);
268 implicitFeaturesMap[userKeyString] = (userFeature[0] == '+');
271 auto HasError =
272 llvm::AMDGPU::insertWaveSizeFeature(cpu, triple, implicitFeaturesMap);
273 if (HasError.first) {
274 unsigned diagID = diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
275 "Unsupported feature ID: %0");
276 diags.Report(diagID) << HasError.second;
277 return std::string();
280 llvm::SmallVector<std::string> featuresVec;
281 for (auto &implicitFeatureItem : implicitFeaturesMap) {
282 featuresVec.push_back((llvm::Twine(implicitFeatureItem.second ? "+" : "-") +
283 implicitFeatureItem.first().str())
284 .str());
286 llvm::sort(featuresVec);
287 return llvm::join(featuresVec, ",");
290 // Get feature string which represents combined explicit target features
291 // for NVPTX and the target features specified by the user/
292 // TODO: Have a more robust target conf like `clang/lib/Basic/Targets/NVPTX.cpp`
293 static std::string
294 getExplicitAndImplicitNVPTXTargetFeatures(clang::DiagnosticsEngine &diags,
295 const TargetOptions &targetOpts,
296 const llvm::Triple triple) {
297 llvm::StringRef cpu = targetOpts.cpu;
298 llvm::StringMap<bool> implicitFeaturesMap;
299 std::string errorMsg;
300 bool ptxVer = false;
302 // Add target features specified by the user
303 for (auto &userFeature : targetOpts.featuresAsWritten) {
304 llvm::StringRef userKeyString(llvm::StringRef(userFeature).drop_front(1));
305 implicitFeaturesMap[userKeyString.str()] = (userFeature[0] == '+');
306 // Check if the user provided a PTX version
307 if (userKeyString.starts_with("ptx"))
308 ptxVer = true;
311 // Set the default PTX version to `ptx61` if none was provided.
312 // TODO: set the default PTX version based on the chip.
313 if (!ptxVer)
314 implicitFeaturesMap["ptx61"] = true;
316 // Set the compute capability.
317 implicitFeaturesMap[cpu.str()] = true;
319 llvm::SmallVector<std::string> featuresVec;
320 for (auto &implicitFeatureItem : implicitFeaturesMap) {
321 featuresVec.push_back((llvm::Twine(implicitFeatureItem.second ? "+" : "-") +
322 implicitFeatureItem.first().str())
323 .str());
325 llvm::sort(featuresVec);
326 return llvm::join(featuresVec, ",");
329 std::string CompilerInstance::getTargetFeatures() {
330 const TargetOptions &targetOpts = getInvocation().getTargetOpts();
331 const llvm::Triple triple(targetOpts.triple);
333 // Clang does not append all target features to the clang -cc1 invocation.
334 // Some target features are parsed implicitly by clang::TargetInfo child
335 // class. Clang::TargetInfo classes are the basic clang classes and
336 // they cannot be reused by Flang.
337 // That's why we need to extract implicit target features and add
338 // them to the target features specified by the user
339 if (triple.isAMDGPU()) {
340 return getExplicitAndImplicitAMDGPUTargetFeatures(getDiagnostics(),
341 targetOpts, triple);
342 } else if (triple.isNVPTX()) {
343 return getExplicitAndImplicitNVPTXTargetFeatures(getDiagnostics(),
344 targetOpts, triple);
346 return llvm::join(targetOpts.featuresAsWritten.begin(),
347 targetOpts.featuresAsWritten.end(), ",");
350 bool CompilerInstance::setUpTargetMachine() {
351 const TargetOptions &targetOpts = getInvocation().getTargetOpts();
352 const std::string &theTriple = targetOpts.triple;
354 // Create `Target`
355 std::string error;
356 const llvm::Target *theTarget =
357 llvm::TargetRegistry::lookupTarget(theTriple, error);
358 if (!theTarget) {
359 getDiagnostics().Report(clang::diag::err_fe_unable_to_create_target)
360 << error;
361 return false;
363 // Create `TargetMachine`
364 const auto &CGOpts = getInvocation().getCodeGenOpts();
365 std::optional<llvm::CodeGenOptLevel> OptLevelOrNone =
366 llvm::CodeGenOpt::getLevel(CGOpts.OptimizationLevel);
367 assert(OptLevelOrNone && "Invalid optimization level!");
368 llvm::CodeGenOptLevel OptLevel = *OptLevelOrNone;
369 std::string featuresStr = getTargetFeatures();
370 std::optional<llvm::CodeModel::Model> cm = getCodeModel(CGOpts.CodeModel);
372 llvm::TargetOptions tOpts = llvm::TargetOptions();
373 tOpts.EnableAIXExtendedAltivecABI = targetOpts.EnableAIXExtendedAltivecABI;
375 targetMachine.reset(theTarget->createTargetMachine(
376 theTriple, /*CPU=*/targetOpts.cpu,
377 /*Features=*/featuresStr, /*Options=*/tOpts,
378 /*Reloc::Model=*/CGOpts.getRelocationModel(),
379 /*CodeModel::Model=*/cm, OptLevel));
380 assert(targetMachine && "Failed to create TargetMachine");
381 if (cm.has_value()) {
382 const llvm::Triple triple(theTriple);
383 if ((cm == llvm::CodeModel::Medium || cm == llvm::CodeModel::Large) &&
384 triple.getArch() == llvm::Triple::x86_64) {
385 targetMachine->setLargeDataThreshold(CGOpts.LargeDataThreshold);
388 return true;