[BatchMode] Add batch-mode support methods to ToolChain.

This commit is contained in:
Graydon Hoare
2018-01-23 14:32:42 -08:00
parent 798f43cf5f
commit 2bf7a7967a
2 changed files with 189 additions and 0 deletions

View File

@@ -21,6 +21,7 @@
#include "swift/Driver/Compilation.h"
#include "swift/Driver/Driver.h"
#include "swift/Driver/Job.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
@@ -150,6 +151,170 @@ types::ID ToolChain::lookupTypeForExtension(StringRef Ext) const {
return types::lookupTypeForExtension(Ext);
}
/// Return a _single_ TY_Swift InputAction, if one exists;
/// if 0 or >1 such inputs exist, return nullptr.
static const InputAction*
findSingleSwiftInput(const CompileJobAction *CJA) {
auto Inputs = CJA->getInputs();
const InputAction *IA = nullptr;
for (auto const *I : Inputs) {
if (auto const *S = dyn_cast<InputAction>(I)) {
if (S->getType() == types::TY_Swift) {
if (IA == nullptr) {
IA = S;
} else {
// Already found one, two is too many.
return nullptr;
}
}
}
}
return IA;
}
static bool
jobsHaveSameExecutableNames(const Job *A, const Job *B) {
// Jobs that get here (that are derived from CompileJobActions) should always
// have the same executable name -- it should always be SWIFT_EXECUTABLE_NAME
// -- but we check here just to be sure / fail gracefully in non-assert
// builds.
assert(strcmp(A->getExecutable(), B->getExecutable()) == 0);
if (strcmp(A->getExecutable(), B->getExecutable()) != 0) {
return false;
}
return true;
}
static bool
jobsHaveSameOutputTypes(const Job *A, const Job *B) {
if (A->getOutput().getPrimaryOutputType() !=
B->getOutput().getPrimaryOutputType())
return false;
return A->getOutput().hasSameAdditionalOutputTypes(B->getOutput());
}
static bool
jobsHaveSameEnvironment(const Job *A, const Job *B) {
auto AEnv = A->getExtraEnvironment();
auto BEnv = B->getExtraEnvironment();
if (AEnv.size() != BEnv.size())
return false;
for (size_t i = 0; i < AEnv.size(); ++i) {
if (strcmp(AEnv[i].first, BEnv[i].first) != 0)
return false;
if (strcmp(AEnv[i].second, BEnv[i].second) != 0)
return false;
}
return true;
}
bool
ToolChain::jobIsBatchable(const Compilation &C, const Job *A) const {
// FIXME: There might be a tighter criterion to use here?
if (C.getOutputInfo().CompilerMode != OutputInfo::Mode::StandardCompile)
return false;
auto const *CJActA = dyn_cast<const CompileJobAction>(&A->getSource());
if (!CJActA)
return false;
return findSingleSwiftInput(CJActA) != nullptr;
}
bool
ToolChain::jobsAreBatchCombinable(const Compilation &C,
const Job *A, const Job *B) const {
assert(jobIsBatchable(C, A));
assert(jobIsBatchable(C, B));
return (jobsHaveSameExecutableNames(A, B) &&
jobsHaveSameOutputTypes(A, B) &&
jobsHaveSameEnvironment(A, B));
}
/// Form a synthetic \c CommandOutput for a \c BatchJob by merging together the
/// \c CommandOutputs of all the jobs passed.
static std::unique_ptr<CommandOutput>
makeBatchCommandOutput(ArrayRef<const Job *> jobs, Compilation &C,
types::ID outputType) {
auto output =
llvm::make_unique<CommandOutput>(outputType, C.getDerivedOutputFileMap());
for (auto const *J : jobs) {
output->addOutputs(J->getOutput());
}
return output;
}
/// Set-union the \c Inputs and \c Actions from each \c Job in \p jobs into the
/// provided \p inputJobs and \p inputActions vectors, further adding all \c
/// Actions from the resulting merger to \p batchCJA. Do set-union rather than
/// concatenation here to avoid mentioning the same input multiple times.
static bool
mergeBatchInputs(ArrayRef<const Job *> jobs,
llvm::SmallSetVector<const Job *, 16> &inputJobs,
llvm::SmallSetVector<const Action *, 16> &inputActions,
CompileJobAction *batchCJA) {
for (auto const *J : jobs) {
for (auto const *I : J->getInputs()) {
inputJobs.insert(I);
}
auto const *CJA = dyn_cast<CompileJobAction>(&J->getSource());
if (!CJA)
return true;
for (auto const *I : CJA->getInputs()) {
inputActions.insert(I);
}
}
for (auto const *I : inputActions) {
batchCJA->addInput(I);
}
return false;
}
/// Construct a \c BatchJob by merging the constituent \p jobs' CommandOutput,
/// input \c Job and \c Action members. Call through to \c constructInvocation
/// on \p BatchJob, to build the \c InvocationInfo.
std::unique_ptr<Job>
ToolChain::constructBatchJob(ArrayRef<const Job *> jobs,
Compilation &C) const
{
#ifndef NDEBUG
// Verify that the equivalence relation on the jobs also holds pairwise.
for (auto *A : jobs) {
for (auto *B : jobs) {
assert(jobsAreBatchCombinable(C, A, B));
}
}
#endif
if (jobs.size() == 0)
return nullptr;
// Synthetic OutputInfo is a slightly-modified version of the initial
// compilation's OI.
auto OI = C.getOutputInfo();
OI.CompilerMode = OutputInfo::Mode::BatchModeCompile;
auto const *executablePath = jobs[0]->getExecutable();
auto outputType = jobs[0]->getOutput().getPrimaryOutputType();
auto output = makeBatchCommandOutput(jobs, C, outputType);
llvm::SmallSetVector<const Job *, 16> inputJobs;
llvm::SmallSetVector<const Action *, 16> inputActions;
auto *batchCJA = C.createAction<CompileJobAction>(outputType);
if (mergeBatchInputs(jobs, inputJobs, inputActions, batchCJA))
return nullptr;
JobContext context{C, inputJobs.getArrayRef(), inputActions.getArrayRef(),
*output, OI};
auto invocationInfo = constructInvocation(*batchCJA, context);
return llvm::make_unique<BatchJob>(*batchCJA,
inputJobs.takeVector(),
std::move(output),
executablePath,
std::move(invocationInfo.Arguments),
std::move(invocationInfo.ExtraEnvironment),
std::move(invocationInfo.FilelistInfos),
jobs);
}
bool
ToolChain::sanitizerRuntimeLibExists(const ArgList &args,
StringRef sanitizerName) const {