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