steven_wu updated this revision to Diff 121779. steven_wu added a comment. This seems to be the cleanest solution I can find for CUDA without touching the job configuration for CUDA. My proposed fix is to bail out of the jobs that are parts of CUDA offload if some command failed before that. Now if you hit failures when compiling multiple GPU architectures, you will error out only once. This is pretty much the exact same behavior of current driver.
This is not the ideal solution but there is simply no information in the job setup in current CUDA driver to detect if the input is same across different jobs. For all the CUDA inputs, it is duplicated into different InputActions and insert into different OffloadActions. You also cannot detect them by looking at their name because the following should compiled the same file twice even there are errors in it: cc a.c a.c ` Also, the reason I don't know how to craft a testcase is not because I have trouble with CUDA driver, but how to write a test to check when did the driver bailed out. Let me know if you have any suggestions. https://reviews.llvm.org/D39502 Files: lib/Driver/Compilation.cpp test/Driver/output-file-cleanup.c test/Driver/unix-conformance.c
Index: test/Driver/unix-conformance.c =================================================================== --- /dev/null +++ test/Driver/unix-conformance.c @@ -0,0 +1,24 @@ +// Check UNIX conformance for cc/c89/c99 +// When c99 encounters a compilation error that causes an object file not to be +// created, it shall write a diagnostic to standard error and continue to +// compile other source code operands, but it shall not perform the link phase +// and it shall return a non-zero exit status. + +// When given multiple .c files to compile, clang compiles them in order until +// it hits an error, at which point it stops. +// +// RUN: rm -rf %t-dir +// RUN: mkdir -p %t-dir +// RUN: cd %t-dir +// +// RUN: touch %t-dir/1.c +// RUN: echo "invalid C code" > %t-dir/2.c +// RUN: touch %t-dir/3.c +// RUN: echo "invalid C code" > %t-dir/4.c +// RUN: touch %t-dir/5.c +// RUN: not %clang -S %t-dir/1.c %t-dir/2.c %t-dir/3.c %t-dir/4.c %t-dir/5.c +// RUN: test -f %t-dir/1.s +// RUN: test ! -f %t-dir/2.s +// RUN: test -f %t-dir/3.s +// RUN: test ! -f %t-dir/4.s +// RUN: test -f %t-dir/5.s Index: test/Driver/output-file-cleanup.c =================================================================== --- test/Driver/output-file-cleanup.c +++ test/Driver/output-file-cleanup.c @@ -41,18 +41,3 @@ // RUN: not %clang -S %t-dir/1.c %t-dir/2.c // RUN: test -f %t-dir/1.s // RUN: test ! -f %t-dir/2.s - -// When given multiple .c files to compile, clang compiles them in order until -// it hits an error, at which point it stops. -// -// RUN: touch %t-dir/1.c -// RUN: echo "invalid C code" > %t-dir/2.c -// RUN: touch %t-dir/3.c -// RUN: echo "invalid C code" > %t-dir/4.c -// RUN: touch %t-dir/5.c -// RUN: not %clang -S %t-dir/1.c %t-dir/2.c %t-dir/3.c %t-dir/4.c %t-dir/5.c -// RUN: test -f %t-dir/1.s -// RUN: test ! -f %t-dir/2.s -// RUN: test ! -f %t-dir/3.s -// RUN: test ! -f %t-dir/4.s -// RUN: test ! -f %t-dir/5.s Index: lib/Driver/Compilation.cpp =================================================================== --- lib/Driver/Compilation.cpp +++ lib/Driver/Compilation.cpp @@ -182,16 +182,51 @@ return ExecutionFailed ? 1 : Res; } -void Compilation::ExecuteJobs( - const JobList &Jobs, - SmallVectorImpl<std::pair<int, const Command *>> &FailingCommands) const { +using FailingCommandList = SmallVectorImpl<std::pair<int, const Command *>>; + +static bool ActionFailed(const Action *A, + const FailingCommandList &FailingCommands) { + + if (FailingCommands.empty()) + return false; + + // CUDA can have the same input source code compiled multiple times so do not + // compiled again if there are already failures. It is OK to abort the CUDA + // pipeline on errors. + if (A->isOffloading(Action::OFK_Cuda)) + return true; + + for (const auto &CI : FailingCommands) + if (A == &(CI.second->getSource())) + return true; + + for (const Action *AI : A->inputs()) + if (ActionFailed(AI, FailingCommands)) + return true; + + return false; +} + +static bool InputsOk(const Command &C, + const FailingCommandList &FailingCommands) { + return !ActionFailed(&C.getSource(), FailingCommands); +} + +void Compilation::ExecuteJobs(const JobList &Jobs, + FailingCommandList &FailingCommands) const { + // According to UNIX standard, driver need to continue compiling all the + // inputs on the command line even one of them failed. + // In all but CLMode, execute all the jobs unless the necessary inputs for the + // job is missing due to previous failures. for (const auto &Job : Jobs) { + if (!InputsOk(Job, FailingCommands)) + continue; const Command *FailingCommand = nullptr; if (int Res = ExecuteCommand(Job, FailingCommand)) { FailingCommands.push_back(std::make_pair(Res, FailingCommand)); - // Bail as soon as one command fails, so we don't output duplicate error - // messages if we die on e.g. the same file. - return; + // Bail as soon as one command fails in cl driver mode. + if (TheDriver.IsCLMode()) + return; } } }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits