[MemProf] Templatize CallStackRadixTreeBuilder (NFC) (#117014)
[llvm-project.git] / flang / lib / Frontend / CompilerInstance.cpp
blob35c2ae3c73e69e8c920a6eb4573d60940b4df27b
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 "clang/Basic/DiagnosticFrontend.h"
21 #include "llvm/ADT/StringExtras.h"
22 #include "llvm/MC/TargetRegistry.h"
23 #include "llvm/Support/Errc.h"
24 #include "llvm/Support/Error.h"
25 #include "llvm/Support/FileSystem.h"
26 #include "llvm/Support/Path.h"
27 #include "llvm/Support/raw_ostream.h"
28 #include "llvm/TargetParser/TargetParser.h"
29 #include "llvm/TargetParser/Triple.h"
31 using namespace Fortran::frontend;
33 CompilerInstance::CompilerInstance()
34 : invocation(new CompilerInvocation()),
35 allSources(new Fortran::parser::AllSources()),
36 allCookedSources(new Fortran::parser::AllCookedSources(*allSources)),
37 parsing(new Fortran::parser::Parsing(*allCookedSources)) {
38 // TODO: This is a good default during development, but ultimately we should
39 // give the user the opportunity to specify this.
40 allSources->set_encoding(Fortran::parser::Encoding::UTF_8);
43 CompilerInstance::~CompilerInstance() {
44 assert(outputFiles.empty() && "Still output files in flight?");
47 void CompilerInstance::setInvocation(
48 std::shared_ptr<CompilerInvocation> value) {
49 invocation = std::move(value);
52 void CompilerInstance::setSemaOutputStream(raw_ostream &value) {
53 ownedSemaOutputStream.release();
54 semaOutputStream = &value;
57 void CompilerInstance::setSemaOutputStream(std::unique_ptr<raw_ostream> value) {
58 ownedSemaOutputStream.swap(value);
59 semaOutputStream = ownedSemaOutputStream.get();
62 // Helper method to generate the path of the output file. The following logic
63 // applies:
64 // 1. If the user specifies the output file via `-o`, then use that (i.e.
65 // the outputFilename parameter).
66 // 2. If the user does not specify the name of the output file, derive it from
67 // the input file (i.e. inputFilename + extension)
68 // 3. If the output file is not specified and the input file is `-`, then set
69 // the output file to `-` as well.
70 static std::string getOutputFilePath(llvm::StringRef outputFilename,
71 llvm::StringRef inputFilename,
72 llvm::StringRef extension) {
74 // Output filename _is_ specified. Just use that.
75 if (!outputFilename.empty())
76 return std::string(outputFilename);
78 // Output filename _is not_ specified. Derive it from the input file name.
79 std::string outFile = "-";
80 if (!extension.empty() && (inputFilename != "-")) {
81 llvm::SmallString<128> path(inputFilename);
82 llvm::sys::path::replace_extension(path, extension);
83 outFile = std::string(path);
86 return outFile;
89 std::unique_ptr<llvm::raw_pwrite_stream>
90 CompilerInstance::createDefaultOutputFile(bool binary, llvm::StringRef baseName,
91 llvm::StringRef extension) {
93 // Get the path of the output file
94 std::string outputFilePath =
95 getOutputFilePath(getFrontendOpts().outputFile, baseName, extension);
97 // Create the output file
98 llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>> os =
99 createOutputFileImpl(outputFilePath, binary);
101 // If successful, add the file to the list of tracked output files and
102 // return.
103 if (os) {
104 outputFiles.emplace_back(OutputFile(outputFilePath));
105 return std::move(*os);
108 // If unsuccessful, issue an error and return Null
109 unsigned diagID = getDiagnostics().getCustomDiagID(
110 clang::DiagnosticsEngine::Error, "unable to open output file '%0': '%1'");
111 getDiagnostics().Report(diagID)
112 << outputFilePath << llvm::errorToErrorCode(os.takeError()).message();
113 return nullptr;
116 llvm::Expected<std::unique_ptr<llvm::raw_pwrite_stream>>
117 CompilerInstance::createOutputFileImpl(llvm::StringRef outputFilePath,
118 bool binary) {
120 // Creates the file descriptor for the output file
121 std::unique_ptr<llvm::raw_fd_ostream> os;
123 std::error_code error;
124 os.reset(new llvm::raw_fd_ostream(
125 outputFilePath, error,
126 (binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF)));
127 if (error) {
128 return llvm::errorCodeToError(error);
131 // For seekable streams, just return the stream corresponding to the output
132 // file.
133 if (!binary || os->supportsSeeking())
134 return std::move(os);
136 // For non-seekable streams, we need to wrap the output stream into something
137 // that supports 'pwrite' and takes care of the ownership for us.
138 return std::make_unique<llvm::buffer_unique_ostream>(std::move(os));
141 void CompilerInstance::clearOutputFiles(bool eraseFiles) {
142 for (OutputFile &of : outputFiles)
143 if (!of.filename.empty() && eraseFiles)
144 llvm::sys::fs::remove(of.filename);
146 outputFiles.clear();
149 bool CompilerInstance::executeAction(FrontendAction &act) {
150 auto &invoc = this->getInvocation();
152 llvm::Triple targetTriple{llvm::Triple(invoc.getTargetOpts().triple)};
153 if (targetTriple.getArch() == llvm::Triple::ArchType::x86_64) {
154 invoc.getDefaultKinds().set_quadPrecisionKind(10);
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 // Run the frontend action `act` for every input file.
171 for (const FrontendInputFile &fif : getFrontendOpts().inputs) {
172 if (act.beginSourceFile(*this, fif)) {
173 if (llvm::Error err = act.execute()) {
174 consumeError(std::move(err));
176 act.endSourceFile();
179 return !getDiagnostics().getClient()->getNumErrors();
182 void CompilerInstance::createDiagnostics(clang::DiagnosticConsumer *client,
183 bool shouldOwnClient) {
184 diagnostics =
185 createDiagnostics(&getDiagnosticOpts(), client, shouldOwnClient);
188 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine>
189 CompilerInstance::createDiagnostics(clang::DiagnosticOptions *opts,
190 clang::DiagnosticConsumer *client,
191 bool shouldOwnClient) {
192 clang::IntrusiveRefCntPtr<clang::DiagnosticIDs> diagID(
193 new clang::DiagnosticIDs());
194 clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diags(
195 new clang::DiagnosticsEngine(diagID, opts));
197 // Create the diagnostic client for reporting errors or for
198 // implementing -verify.
199 if (client) {
200 diags->setClient(client, shouldOwnClient);
201 } else {
202 diags->setClient(new TextDiagnosticPrinter(llvm::errs(), opts));
204 return diags;
207 // Get feature string which represents combined explicit target features
208 // for AMD GPU and the target features specified by the user
209 static std::string
210 getExplicitAndImplicitAMDGPUTargetFeatures(clang::DiagnosticsEngine &diags,
211 const TargetOptions &targetOpts,
212 const llvm::Triple triple) {
213 llvm::StringRef cpu = targetOpts.cpu;
214 llvm::StringMap<bool> implicitFeaturesMap;
215 // Get the set of implicit target features
216 llvm::AMDGPU::fillAMDGPUFeatureMap(cpu, triple, implicitFeaturesMap);
218 // Add target features specified by the user
219 for (auto &userFeature : targetOpts.featuresAsWritten) {
220 std::string userKeyString = userFeature.substr(1);
221 implicitFeaturesMap[userKeyString] = (userFeature[0] == '+');
224 auto HasError =
225 llvm::AMDGPU::insertWaveSizeFeature(cpu, triple, implicitFeaturesMap);
226 if (HasError.first) {
227 unsigned diagID = diags.getCustomDiagID(clang::DiagnosticsEngine::Error,
228 "Unsupported feature ID: %0");
229 diags.Report(diagID) << HasError.second;
230 return std::string();
233 llvm::SmallVector<std::string> featuresVec;
234 for (auto &implicitFeatureItem : implicitFeaturesMap) {
235 featuresVec.push_back((llvm::Twine(implicitFeatureItem.second ? "+" : "-") +
236 implicitFeatureItem.first().str())
237 .str());
239 llvm::sort(featuresVec);
240 return llvm::join(featuresVec, ",");
243 // Get feature string which represents combined explicit target features
244 // for NVPTX and the target features specified by the user/
245 // TODO: Have a more robust target conf like `clang/lib/Basic/Targets/NVPTX.cpp`
246 static std::string
247 getExplicitAndImplicitNVPTXTargetFeatures(clang::DiagnosticsEngine &diags,
248 const TargetOptions &targetOpts,
249 const llvm::Triple triple) {
250 llvm::StringRef cpu = targetOpts.cpu;
251 llvm::StringMap<bool> implicitFeaturesMap;
252 std::string errorMsg;
253 bool ptxVer = false;
255 // Add target features specified by the user
256 for (auto &userFeature : targetOpts.featuresAsWritten) {
257 llvm::StringRef userKeyString(llvm::StringRef(userFeature).drop_front(1));
258 implicitFeaturesMap[userKeyString.str()] = (userFeature[0] == '+');
259 // Check if the user provided a PTX version
260 if (userKeyString.starts_with("ptx"))
261 ptxVer = true;
264 // Set the default PTX version to `ptx61` if none was provided.
265 // TODO: set the default PTX version based on the chip.
266 if (!ptxVer)
267 implicitFeaturesMap["ptx61"] = true;
269 // Set the compute capability.
270 implicitFeaturesMap[cpu.str()] = true;
272 llvm::SmallVector<std::string> featuresVec;
273 for (auto &implicitFeatureItem : implicitFeaturesMap) {
274 featuresVec.push_back((llvm::Twine(implicitFeatureItem.second ? "+" : "-") +
275 implicitFeatureItem.first().str())
276 .str());
278 llvm::sort(featuresVec);
279 return llvm::join(featuresVec, ",");
282 std::string CompilerInstance::getTargetFeatures() {
283 const TargetOptions &targetOpts = getInvocation().getTargetOpts();
284 const llvm::Triple triple(targetOpts.triple);
286 // Clang does not append all target features to the clang -cc1 invocation.
287 // Some target features are parsed implicitly by clang::TargetInfo child
288 // class. Clang::TargetInfo classes are the basic clang classes and
289 // they cannot be reused by Flang.
290 // That's why we need to extract implicit target features and add
291 // them to the target features specified by the user
292 if (triple.isAMDGPU()) {
293 return getExplicitAndImplicitAMDGPUTargetFeatures(getDiagnostics(),
294 targetOpts, triple);
295 } else if (triple.isNVPTX()) {
296 return getExplicitAndImplicitNVPTXTargetFeatures(getDiagnostics(),
297 targetOpts, triple);
299 return llvm::join(targetOpts.featuresAsWritten.begin(),
300 targetOpts.featuresAsWritten.end(), ",");
303 bool CompilerInstance::setUpTargetMachine() {
304 const TargetOptions &targetOpts = getInvocation().getTargetOpts();
305 const std::string &theTriple = targetOpts.triple;
307 // Create `Target`
308 std::string error;
309 const llvm::Target *theTarget =
310 llvm::TargetRegistry::lookupTarget(theTriple, error);
311 if (!theTarget) {
312 getDiagnostics().Report(clang::diag::err_fe_unable_to_create_target)
313 << error;
314 return false;
316 // Create `TargetMachine`
317 const auto &CGOpts = getInvocation().getCodeGenOpts();
318 std::optional<llvm::CodeGenOptLevel> OptLevelOrNone =
319 llvm::CodeGenOpt::getLevel(CGOpts.OptimizationLevel);
320 assert(OptLevelOrNone && "Invalid optimization level!");
321 llvm::CodeGenOptLevel OptLevel = *OptLevelOrNone;
322 std::string featuresStr = getTargetFeatures();
323 std::optional<llvm::CodeModel::Model> cm = getCodeModel(CGOpts.CodeModel);
325 llvm::TargetOptions tOpts = llvm::TargetOptions();
326 tOpts.EnableAIXExtendedAltivecABI = targetOpts.EnableAIXExtendedAltivecABI;
328 targetMachine.reset(theTarget->createTargetMachine(
329 theTriple, /*CPU=*/targetOpts.cpu,
330 /*Features=*/featuresStr, /*Options=*/tOpts,
331 /*Reloc::Model=*/CGOpts.getRelocationModel(),
332 /*CodeModel::Model=*/cm, OptLevel));
333 assert(targetMachine && "Failed to create TargetMachine");
334 if (cm.has_value()) {
335 const llvm::Triple triple(theTriple);
336 if ((cm == llvm::CodeModel::Medium || cm == llvm::CodeModel::Large) &&
337 triple.getArch() == llvm::Triple::x86_64) {
338 targetMachine->setLargeDataThreshold(CGOpts.LargeDataThreshold);
341 return true;