clang 24.0.0git
Compilation.cpp
Go to the documentation of this file.
1//===- Compilation.cpp - Compilation Task Implementation ------------------===//
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
10#include "clang/Basic/LLVM.h"
11#include "clang/Driver/Action.h"
13#include "clang/Driver/Driver.h"
14#include "clang/Driver/Job.h"
16#include "clang/Driver/Util.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/Option/ArgList.h"
20#include "llvm/Option/OptSpecifier.h"
21#include "llvm/Option/Option.h"
22#include "llvm/Support/FileSystem.h"
23#include "llvm/Support/ThreadPool.h"
24#include "llvm/Support/Threading.h"
25#include "llvm/Support/raw_ostream.h"
26#include "llvm/TargetParser/Triple.h"
27#include <algorithm>
28#include <cassert>
29#include <optional>
30#include <string>
31#include <system_error>
32#include <utility>
33
34using namespace clang;
35using namespace driver;
36using namespace llvm::opt;
37
38Compilation::Compilation(const Driver &D, const ToolChain &_DefaultToolChain,
39 InputArgList *_Args, DerivedArgList *_TranslatedArgs,
40 bool ContainsError)
41 : TheDriver(D), DefaultToolChain(_DefaultToolChain), Args(_Args),
42 TranslatedArgs(_TranslatedArgs), ContainsError(ContainsError) {
43 // The offloading host toolchain is the default toolchain.
44 OrderedOffloadingToolchains.insert(
45 std::make_pair(Action::OFK_Host, &DefaultToolChain));
46}
47
49 // Remove temporary files. This must be done before arguments are freed, as
50 // the file names might be derived from the input arguments.
51 if (!TheDriver.isSaveTempsEnabled() && !ForceKeepTempFiles)
52 CleanupFileList(TempFiles);
53
54 delete TranslatedArgs;
55 delete Args;
56
57 // Free any derived arg lists.
58 for (auto Arg : TCArgs)
59 if (Arg.second != TranslatedArgs)
60 delete Arg.second;
61}
62
63const DerivedArgList &
65 Action::OffloadKind DeviceOffloadKind) {
66 if (!TC)
67 TC = &DefaultToolChain;
68
69 DerivedArgList *&Entry = TCArgs[{TC, BA, DeviceOffloadKind}];
70 if (!Entry) {
71 SmallVector<Arg *, 4> AllocatedArgs;
72 DerivedArgList *OpenMPArgs = nullptr;
73 // Translate OpenMP toolchain arguments provided via the -Xopenmp-target flags.
74 if (DeviceOffloadKind == Action::OFK_OpenMP) {
76 bool SameTripleAsHost = (TC->getTriple() == HostTC->getTriple());
77 OpenMPArgs = TC->TranslateOpenMPTargetArgs(
78 *TranslatedArgs, SameTripleAsHost, AllocatedArgs);
79 }
80
81 DerivedArgList *NewDAL = nullptr;
82 if (!OpenMPArgs) {
83 NewDAL = TC->TranslateXarchArgs(*TranslatedArgs, BA, DeviceOffloadKind,
84 &AllocatedArgs);
85 } else {
86 NewDAL = TC->TranslateXarchArgs(*OpenMPArgs, BA, DeviceOffloadKind,
87 &AllocatedArgs);
88 if (!NewDAL)
89 NewDAL = OpenMPArgs;
90 else
91 delete OpenMPArgs;
92 }
93
94 if (!NewDAL) {
95 Entry = TC->TranslateArgs(*TranslatedArgs, BA, DeviceOffloadKind);
96 if (!Entry)
97 Entry = TranslatedArgs;
98 } else {
99 Entry = TC->TranslateArgs(*NewDAL, BA, DeviceOffloadKind);
100 if (!Entry)
101 Entry = NewDAL;
102 else
103 delete NewDAL;
104 }
105
106 // Add allocated arguments to the final DAL.
107 for (auto *ArgPtr : AllocatedArgs)
108 Entry->AddSynthesizedArg(ArgPtr);
109 }
110
111 return *Entry;
112}
113
114bool Compilation::CleanupFile(const char *File, bool IssueErrors) const {
115 // FIXME: Why are we trying to remove files that we have not created? For
116 // example we should only try to remove a temporary assembly file if
117 // "clang -cc1" succeed in writing it. Was this a workaround for when
118 // clang was writing directly to a .s file and sometimes leaving it behind
119 // during a failure?
120
121 // FIXME: If this is necessary, we can still try to split
122 // llvm::sys::fs::remove into a removeFile and a removeDir and avoid the
123 // duplicated stat from is_regular_file.
124
125 // Don't try to remove files which we don't have write access to (but may be
126 // able to remove), or non-regular files. Underlying tools may have
127 // intentionally not overwritten them.
128 if (!llvm::sys::fs::can_write(File) || !llvm::sys::fs::is_regular_file(File))
129 return true;
130
131 if (std::error_code EC = llvm::sys::fs::remove(File)) {
132 // Failure is only failure if the file exists and is "regular". We checked
133 // for it being regular before, and llvm::sys::fs::remove ignores ENOENT,
134 // so we don't need to check again.
135
136 if (IssueErrors)
137 getDriver().Diag(diag::err_drv_unable_to_remove_file)
138 << EC.message();
139 return false;
140 }
141 return true;
142}
143
144bool Compilation::CleanupFileList(const llvm::opt::ArgStringList &Files,
145 bool IssueErrors) const {
146 bool Success = true;
147 for (const auto &File: Files)
148 Success &= CleanupFile(File, IssueErrors);
149 return Success;
150}
151
153 const JobAction *JA,
154 bool IssueErrors) const {
155 bool Success = true;
156 for (const auto &File : Files) {
157 // If specified, only delete the files associated with the JobAction.
158 // Otherwise, delete all files in the map.
159 if (JA && File.first != JA)
160 continue;
161 Success &= CleanupFile(File.second, IssueErrors);
162 }
163 return Success;
164}
165
167 const Command *&FailingCommand,
168 bool LogOnly) const {
169 if ((getDriver().CCPrintOptions ||
170 getArgs().hasArg(options::OPT_v)) && !getDriver().CCGenDiagnostics) {
171 raw_ostream *OS = &llvm::errs();
172 std::unique_ptr<llvm::raw_fd_ostream> OwnedStream;
173
174 // Follow gcc implementation of CC_PRINT_OPTIONS; we could also cache the
175 // output stream.
176 if (getDriver().CCPrintOptions &&
177 !getDriver().CCPrintOptionsFilename.empty()) {
178 std::error_code EC;
179 OwnedStream.reset(new llvm::raw_fd_ostream(
180 getDriver().CCPrintOptionsFilename, EC,
181 llvm::sys::fs::OF_Append | llvm::sys::fs::OF_TextWithCRLF));
182 if (EC) {
183 getDriver().Diag(diag::err_drv_cc_print_options_failure)
184 << EC.message();
185 FailingCommand = &C;
186 return 1;
187 }
188 OS = OwnedStream.get();
189 }
190
191 if (getDriver().CCPrintOptions)
192 *OS << "[Logging clang options]\n";
193
194 C.Print(*OS, "\n", /*Quote=*/getDriver().CCPrintOptions);
195 }
196
197 if (LogOnly)
198 return 0;
199
200 std::string Error;
201 bool ExecutionFailed;
202 int Res = C.Execute(Redirects, &Error, &ExecutionFailed);
203 if (PostCallback)
204 PostCallback(C, Res);
205 if (!Error.empty()) {
206 assert(Res && "Error string set with 0 result code!");
207 getDriver().Diag(diag::err_drv_command_failure) << Error;
208 }
209
210 if (Res)
211 FailingCommand = &C;
212
213 return ExecutionFailed ? 1 : Res;
214}
215
217
218static bool ActionFailed(const Action *A,
219 const FailingCommandList &FailingCommands) {
220 if (FailingCommands.empty())
221 return false;
222
223 // CUDA/HIP/SYCL can have the same input source code compiled multiple times
224 // so do not compile again if there are already failures. It is OK to abort
225 // the CUDA/HIP/SYCL pipeline on errors.
228 return true;
229
230 for (const auto &CI : FailingCommands)
231 if (A == &(CI.second->getSource()))
232 return true;
233
234 for (const auto *AI : A->inputs())
235 if (ActionFailed(AI, FailingCommands))
236 return true;
237
238 return false;
239}
240
241static bool ActionDependsOn(const Action *A, const Action *Other) {
242 return A == Other || llvm::any_of(A->inputs(), [&](const Action *Input) {
243 return ActionDependsOn(Input, Other);
244 });
245}
246
247static bool ActionsAreIndependent(const Action *A, const Action *B) {
248 return !ActionDependsOn(A, B) && !ActionDependsOn(B, A);
249}
250
252 return !Job.InProcess && !Job.PrintInputFilenames &&
253 !Job.getBoundArch().empty() &&
255}
256
261
262static bool HasDistinctBoundArch(const Command &Candidate,
264 BoundArch CandidateArch = Candidate.getBoundArch();
265 return llvm::none_of(Jobs, [&](const Command *Job) {
266 return Job->getBoundArch() == CandidateArch;
267 });
268}
269
270static std::optional<llvm::ThreadPoolStrategy>
271getParallelOffloadJobsStrategy(const ArgList &Args, unsigned NumJobs) {
272 if (NumJobs < 2)
273 return std::nullopt;
274
275 auto OffloadJobs = tools::parseOffloadJobs(Args);
276 if (!OffloadJobs.isValid())
277 return std::nullopt;
278
279 if (OffloadJobs.K == tools::OffloadJobsOpt::Kind::Jobserver)
280 return llvm::jobserver_concurrency();
281
282 if (OffloadJobs.NumThreads < 2)
283 return std::nullopt;
284
285 return llvm::hardware_concurrency(std::min(OffloadJobs.NumThreads, NumJobs));
286}
287
289 int Res = 0;
290 bool ExecutionFailed = false;
291 std::string Error;
292};
293
297
298static std::optional<ParallelOffloadJobGroupResult>
299tryExecuteParallelOffloadJobGroup(const Driver &D, const ArgList &Args,
300 ArrayRef<std::optional<StringRef>> Redirects,
301 const JobList::list_type &JobStorage,
302 size_t StartIndex,
303 FailingCommandList &FailingCommands) {
304 const Command &Job = *JobStorage[StartIndex];
306 return std::nullopt;
307
309 for (size_t I = StartIndex; I < JobStorage.size(); ++I) {
310 const Command &Candidate = *JobStorage[I];
311 if (ActionFailed(&Candidate.getSource(), FailingCommands))
312 break;
313
314 if (!CanRunInParallelOffloadJobGroup(Candidate))
315 break;
316
317 if (!SameParallelOffloadJobGroup(Job, Candidate))
318 break;
319
320 if (!HasDistinctBoundArch(Candidate, ParallelJobs))
321 break;
322
323 if (!llvm::all_of(ParallelJobs, [&](const Command *Other) {
324 return ActionsAreIndependent(&Candidate.getSource(),
325 &Other->getSource());
326 }))
327 break;
328
329 ParallelJobs.push_back(&Candidate);
330 }
331
332 std::optional<llvm::ThreadPoolStrategy> Strategy =
333 getParallelOffloadJobsStrategy(Args, ParallelJobs.size());
334 if (!Strategy)
335 return std::nullopt;
336
337 SmallVector<ParallelJobResult, 4> Results(ParallelJobs.size());
338 llvm::DefaultThreadPool Pool(*Strategy);
339 for (auto IndexedJob : llvm::enumerate(ParallelJobs)) {
340 size_t Index = IndexedJob.index();
341 const Command *ParallelJob = IndexedJob.value();
342 Pool.async([&, Index, ParallelJob] {
343 Results[Index].Res = ParallelJob->Execute(
344 Redirects, &Results[Index].Error, &Results[Index].ExecutionFailed);
345 });
346 }
347 Pool.wait();
348
349 for (auto [Index, ParallelJob] : llvm::enumerate(ParallelJobs)) {
350 ParallelJobResult &Result = Results[Index];
351 if (!Result.Error.empty()) {
352 assert(Result.Res && "Error string set with 0 result code!");
353 D.Diag(diag::err_drv_command_failure) << Result.Error;
354 }
355
356 if (Result.Res) {
357 FailingCommands.push_back(
358 std::make_pair(Result.ExecutionFailed ? 1 : Result.Res, ParallelJob));
359 }
360 }
361
362 return ParallelOffloadJobGroupResult{ParallelJobs.size()};
363}
364
366 FailingCommandList &FailingCommands,
367 bool LogOnly) const {
368 // According to UNIX standard, driver need to continue compiling all the
369 // inputs on the command line even one of them failed.
370 // In all but CLMode, execute all the jobs unless the necessary inputs for the
371 // job is missing due to previous failures.
372 bool CanRunJobsInParallel =
373 !LogOnly && !getDriver().CCPrintOptions &&
375 !getDriver().IsCLMode() && !getArgs().hasArg(options::OPT_v) &&
376 Redirects.empty() && !PostCallback;
377
378 const auto &JobStorage = Jobs.getJobs();
379 for (size_t I = 0; I < JobStorage.size();) {
380 const auto &Job = *JobStorage[I];
381 if (ActionFailed(&Job.getSource(), FailingCommands)) {
382 ++I;
383 continue;
384 }
385
386 if (CanRunJobsInParallel) {
387 if (std::optional<ParallelOffloadJobGroupResult> Result =
389 Redirects, JobStorage, I,
390 FailingCommands)) {
391 I += Result->NumJobs;
392 continue;
393 }
394 }
395
396 const Command *FailingCommand = nullptr;
397 if (int Res = ExecuteCommand(Job, FailingCommand, LogOnly)) {
398 FailingCommands.push_back(std::make_pair(Res, FailingCommand));
399 // Bail as soon as one command fails in cl driver mode.
400 if (TheDriver.IsCLMode())
401 return;
402 }
403 ++I;
404 }
405}
406
408 ForDiagnostics = true;
409
410 // Free actions and jobs.
411 Actions.clear();
412 AllActions.clear();
413 Jobs.clear();
414
415 // Remove temporary files.
416 if (!TheDriver.isSaveTempsEnabled() && !ForceKeepTempFiles)
417 CleanupFileList(TempFiles);
418
419 // Clear temporary/results file lists.
420 TempFiles.clear();
421 ResultFiles.clear();
422 FailureResultFiles.clear();
423
424 // Remove any user specified output. Claim any unclaimed arguments, so as
425 // to avoid emitting warnings about unused args.
426 OptSpecifier OutputOpts[] = {
427 options::OPT_o, options::OPT_MD, options::OPT_MMD, options::OPT_M,
428 options::OPT_MM, options::OPT_MF, options::OPT_MG, options::OPT_MJ,
429 options::OPT_MQ, options::OPT_MT, options::OPT_MV};
430 for (const auto &Opt : OutputOpts) {
431 if (TranslatedArgs->hasArg(Opt))
432 TranslatedArgs->eraseArg(Opt);
433 }
434 TranslatedArgs->ClaimAllArgs();
435
436 // Force re-creation of the toolchain Args, otherwise our modifications just
437 // above will have no effect.
438 for (auto Arg : TCArgs)
439 if (Arg.second != TranslatedArgs)
440 delete Arg.second;
441 TCArgs.clear();
442
443 // Redirect stdout/stderr to /dev/null.
444 Redirects = {std::nullopt, {""}, {""}};
445
446 // Temporary files added by diagnostics should be kept.
447 ForceKeepTempFiles = true;
448}
449
450StringRef Compilation::getSysRoot() const {
451 return getDriver().SysRoot;
452}
453
454void Compilation::Redirect(ArrayRef<std::optional<StringRef>> Redirects) {
455 this->Redirects = Redirects;
456}
static bool ActionFailed(const Action *A, const FailingCommandList &FailingCommands)
static bool HasDistinctBoundArch(const Command &Candidate, ArrayRef< const Command * > Jobs)
static bool ActionDependsOn(const Action *A, const Action *Other)
static bool ActionsAreIndependent(const Action *A, const Action *B)
static std::optional< ParallelOffloadJobGroupResult > tryExecuteParallelOffloadJobGroup(const Driver &D, const ArgList &Args, ArrayRef< std::optional< StringRef > > Redirects, const JobList::list_type &JobStorage, size_t StartIndex, FailingCommandList &FailingCommands)
static bool SameParallelOffloadJobGroup(const Command &A, const Command &B)
SmallVectorImpl< std::pair< int, const Command * > > FailingCommandList
static bool CanRunInParallelOffloadJobGroup(const Command &Job)
static std::optional< llvm::ThreadPoolStrategy > getParallelOffloadJobsStrategy(const ArgList &Args, unsigned NumJobs)
Result
Implement __builtin_bit_cast and related operations.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Action - Represent an abstract compilation step to perform.
Definition Action.h:48
input_range inputs()
Definition Action.h:163
bool isOffloading(OffloadKind OKind) const
Definition Action.h:230
Command - An executable path/name and argument vector to execute.
Definition Job.h:107
const Action & getSource() const
getSource - Return the Action which caused the creation of this job.
Definition Job.h:196
StringRef getOffloadDeviceParallelJobGroup() const
Definition Job.h:205
bool PrintInputFilenames
Whether to print the input filenames when executing.
Definition Job.h:175
bool InProcess
Whether the command will be executed in this process or not.
Definition Job.h:178
BoundArch getBoundArch() const
Return the bound architecture for this command, if any.
Definition Job.h:202
virtual int Execute(ArrayRef< std::optional< StringRef > > Redirects, std::string *ErrMsg, bool *ExecutionFailed) const
Definition Job.cpp:325
int ExecuteCommand(const Command &C, const Command *&FailingCommand, bool LogOnly=false) const
ExecuteCommand - Execute an actual command.
bool CleanupFileMap(const ArgStringMap &Files, const JobAction *JA, bool IssueErrors=false) const
CleanupFileMap - Remove the files in the given map.
bool CleanupFile(const char *File, bool IssueErrors=false) const
CleanupFile - Delete a given file.
Compilation(const Driver &D, const ToolChain &DefaultToolChain, llvm::opt::InputArgList *Args, llvm::opt::DerivedArgList *TranslatedArgs, bool ContainsError)
bool CleanupFileList(const llvm::opt::ArgStringList &Files, bool IssueErrors=false) const
CleanupFileList - Remove the files in the given list.
const llvm::opt::DerivedArgList & getArgsForToolChain(const ToolChain *TC, BoundArch BA, Action::OffloadKind DeviceOffloadKind)
getArgsForToolChain - Return the derived argument list for the tool chain TC (or the default tool cha...
StringRef getSysRoot() const
Returns the sysroot path.
void Redirect(ArrayRef< std::optional< StringRef > > Redirects)
Redirect - Redirect output of this compilation.
void initCompilationForDiagnostics()
initCompilationForDiagnostics - Remove stale state and suppress output so compilation can be reexecut...
const llvm::opt::DerivedArgList & getArgs() const
void ExecuteJobs(const JobList &Jobs, SmallVectorImpl< std::pair< int, const Command * > > &FailingCommands, bool LogOnly=false) const
ExecuteJob - Execute a single job.
const ToolChain * getSingleOffloadToolChain() const
Return an offload toolchain of the provided kind.
const Driver & getDriver() const
Driver - Encapsulate logic for constructing compilation processes from a set of gcc-driver-like comma...
Definition Driver.h:95
std::string SysRoot
sysroot, if present
Definition Driver.h:195
unsigned CCPrintProcessStats
Set CC_PRINT_PROC_STAT mode, which causes the driver to dump performance report to CC_PRINT_PROC_STAT...
Definition Driver.h:278
bool IsCLMode() const
Whether the driver should follow cl.exe like behavior.
Definition Driver.h:231
unsigned CCGenDiagnostics
Whether the driver is generating diagnostics for debugging purposes.
Definition Driver.h:273
DiagnosticBuilder Diag(unsigned DiagID) const
Definition Driver.h:159
unsigned CCPrintOptions
Set CC_PRINT_OPTIONS mode, which is like -v but logs the commands to CCPrintOptionsFilename or to std...
Definition Driver.h:247
JobList - A sequence of jobs to perform.
Definition Job.h:279
SmallVector< std::unique_ptr< Command >, 4 > list_type
Definition Job.h:281
ToolChain - Access to tools for a single platform.
Definition ToolChain.h:96
virtual llvm::opt::DerivedArgList * TranslateOpenMPTargetArgs(const llvm::opt::DerivedArgList &Args, bool SameTripleAsHost, SmallVectorImpl< llvm::opt::Arg * > &AllocatedArgs) const
TranslateOpenMPTargetArgs - Create a new derived argument list for that contains the OpenMP target sp...
virtual llvm::opt::DerivedArgList * TranslateArgs(const llvm::opt::DerivedArgList &Args, BoundArch BA, Action::OffloadKind DeviceOffloadKind) const
TranslateArgs - Create a new derived argument list for any argument translations this ToolChain may w...
Definition ToolChain.h:401
const llvm::Triple & getTriple() const
Definition ToolChain.h:288
virtual void TranslateXarchArgs(const llvm::opt::DerivedArgList &Args, llvm::opt::Arg *&A, llvm::opt::DerivedArgList *DAL, SmallVectorImpl< llvm::opt::Arg * > *AllocatedArgs=nullptr) const
Append the argument following A to DAL assuming A is an Xarch argument.
OffloadJobsOpt parseOffloadJobs(const llvm::opt::ArgList &Args)
llvm::DenseMap< const JobAction *, const char * > ArgStringMap
ArgStringMap - Type used to map a JobAction to its result file.
Definition Util.h:22
The JSON file list parser is used to communicate input to InstallAPI.
@ Success
Annotation was successful.
Definition Parser.h:65
@ Result
The result type of a method or function.
Definition TypeBase.h:906
@ Other
Other implicit parameter.
Definition Decl.h:1774
Represents a bound architecture for offload / multiple architecture compilation.
bool empty() const