1 //===--- CompilerInstance.cpp ---------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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
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
);
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
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();
116 llvm::Expected
<std::unique_ptr
<llvm::raw_pwrite_stream
>>
117 CompilerInstance::createOutputFileImpl(llvm::StringRef outputFilePath
,
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
)));
128 return llvm::errorCodeToError(error
);
131 // For seekable streams, just return the stream corresponding to the output
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
);
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())
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
));
179 return !getDiagnostics().getClient()->getNumErrors();
182 void CompilerInstance::createDiagnostics(clang::DiagnosticConsumer
*client
,
183 bool shouldOwnClient
) {
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.
200 diags
->setClient(client
, shouldOwnClient
);
202 diags
->setClient(new TextDiagnosticPrinter(llvm::errs(), opts
));
207 // Get feature string which represents combined explicit target features
208 // for AMD GPU and the target features specified by the user
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] == '+');
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())
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`
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
;
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"))
264 // Set the default PTX version to `ptx61` if none was provided.
265 // TODO: set the default PTX version based on the chip.
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())
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(),
295 } else if (triple
.isNVPTX()) {
296 return getExplicitAndImplicitNVPTXTargetFeatures(getDiagnostics(),
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
;
309 const llvm::Target
*theTarget
=
310 llvm::TargetRegistry::lookupTarget(theTriple
, error
);
312 getDiagnostics().Report(clang::diag::err_fe_unable_to_create_target
)
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
);