Skip to content

Commit 2bf7a79

Browse files
committed
[BatchMode] Add batch-mode support methods to ToolChain.
1 parent 798f43c commit 2bf7a79

File tree

2 files changed

+189
-0
lines changed

2 files changed

+189
-0
lines changed

include/swift/Driver/ToolChain.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,30 @@ class ToolChain {
166166
std::unique_ptr<CommandOutput> output,
167167
const OutputInfo &OI) const;
168168

169+
/// Return true iff the input \c Job \p A is an acceptable candidate for
170+
/// batching together into a BatchJob, via a call to \c
171+
/// constructBatchJob. This is true when the \c Job is a built from a \c
172+
/// CompileJobAction in a \c Compilation \p C running in \c
173+
/// OutputInfo::Mode::StandardCompile output mode, with a single \c TY_Swift
174+
/// \c InputAction.
175+
bool jobIsBatchable(const Compilation &C, const Job *A) const;
176+
177+
/// Equivalence relation that holds iff the two input Jobs \p A and \p B are
178+
/// acceptable candidates for combining together into a \c BatchJob, via a
179+
/// call to \c constructBatchJob. This is true when each job independently
180+
/// satisfies \c jobIsBatchable, and the two jobs have identical executables,
181+
/// output types and environments (i.e. they are identical aside from their
182+
/// inputs).
183+
bool jobsAreBatchCombinable(const Compilation &C, const Job *A,
184+
const Job *B) const;
185+
186+
/// Construct a \c BatchJob that subsumes the work of a set of Jobs. Any pair
187+
/// of elements in \p Jobs are assumed to satisfy the equivalence relation \c
188+
/// jobsAreBatchCombinable, i.e. they should all be "the same" job in in all
189+
/// ways other than their choices of inputs.
190+
std::unique_ptr<Job> constructBatchJob(ArrayRef<const Job *> Jobs,
191+
Compilation &C) const;
192+
169193
/// Return the default language type to use for the given extension.
170194
/// If the extension is empty or is otherwise not recognized, return
171195
/// the invalid type \c TY_INVALID.

lib/Driver/ToolChain.cpp

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/Driver/Compilation.h"
2222
#include "swift/Driver/Driver.h"
2323
#include "swift/Driver/Job.h"
24+
#include "llvm/ADT/SetVector.h"
2425
#include "llvm/Option/ArgList.h"
2526
#include "llvm/Support/FileSystem.h"
2627
#include "llvm/Support/Path.h"
@@ -150,6 +151,170 @@ types::ID ToolChain::lookupTypeForExtension(StringRef Ext) const {
150151
return types::lookupTypeForExtension(Ext);
151152
}
152153

154+
/// Return a _single_ TY_Swift InputAction, if one exists;
155+
/// if 0 or >1 such inputs exist, return nullptr.
156+
static const InputAction*
157+
findSingleSwiftInput(const CompileJobAction *CJA) {
158+
auto Inputs = CJA->getInputs();
159+
const InputAction *IA = nullptr;
160+
for (auto const *I : Inputs) {
161+
if (auto const *S = dyn_cast<InputAction>(I)) {
162+
if (S->getType() == types::TY_Swift) {
163+
if (IA == nullptr) {
164+
IA = S;
165+
} else {
166+
// Already found one, two is too many.
167+
return nullptr;
168+
}
169+
}
170+
}
171+
}
172+
return IA;
173+
}
174+
175+
static bool
176+
jobsHaveSameExecutableNames(const Job *A, const Job *B) {
177+
// Jobs that get here (that are derived from CompileJobActions) should always
178+
// have the same executable name -- it should always be SWIFT_EXECUTABLE_NAME
179+
// -- but we check here just to be sure / fail gracefully in non-assert
180+
// builds.
181+
assert(strcmp(A->getExecutable(), B->getExecutable()) == 0);
182+
if (strcmp(A->getExecutable(), B->getExecutable()) != 0) {
183+
return false;
184+
}
185+
return true;
186+
}
187+
188+
static bool
189+
jobsHaveSameOutputTypes(const Job *A, const Job *B) {
190+
if (A->getOutput().getPrimaryOutputType() !=
191+
B->getOutput().getPrimaryOutputType())
192+
return false;
193+
return A->getOutput().hasSameAdditionalOutputTypes(B->getOutput());
194+
}
195+
196+
static bool
197+
jobsHaveSameEnvironment(const Job *A, const Job *B) {
198+
auto AEnv = A->getExtraEnvironment();
199+
auto BEnv = B->getExtraEnvironment();
200+
if (AEnv.size() != BEnv.size())
201+
return false;
202+
for (size_t i = 0; i < AEnv.size(); ++i) {
203+
if (strcmp(AEnv[i].first, BEnv[i].first) != 0)
204+
return false;
205+
if (strcmp(AEnv[i].second, BEnv[i].second) != 0)
206+
return false;
207+
}
208+
return true;
209+
}
210+
211+
bool
212+
ToolChain::jobIsBatchable(const Compilation &C, const Job *A) const {
213+
// FIXME: There might be a tighter criterion to use here?
214+
if (C.getOutputInfo().CompilerMode != OutputInfo::Mode::StandardCompile)
215+
return false;
216+
auto const *CJActA = dyn_cast<const CompileJobAction>(&A->getSource());
217+
if (!CJActA)
218+
return false;
219+
return findSingleSwiftInput(CJActA) != nullptr;
220+
}
221+
222+
bool
223+
ToolChain::jobsAreBatchCombinable(const Compilation &C,
224+
const Job *A, const Job *B) const {
225+
assert(jobIsBatchable(C, A));
226+
assert(jobIsBatchable(C, B));
227+
return (jobsHaveSameExecutableNames(A, B) &&
228+
jobsHaveSameOutputTypes(A, B) &&
229+
jobsHaveSameEnvironment(A, B));
230+
}
231+
232+
/// Form a synthetic \c CommandOutput for a \c BatchJob by merging together the
233+
/// \c CommandOutputs of all the jobs passed.
234+
static std::unique_ptr<CommandOutput>
235+
makeBatchCommandOutput(ArrayRef<const Job *> jobs, Compilation &C,
236+
types::ID outputType) {
237+
auto output =
238+
llvm::make_unique<CommandOutput>(outputType, C.getDerivedOutputFileMap());
239+
for (auto const *J : jobs) {
240+
output->addOutputs(J->getOutput());
241+
}
242+
return output;
243+
}
244+
245+
/// Set-union the \c Inputs and \c Actions from each \c Job in \p jobs into the
246+
/// provided \p inputJobs and \p inputActions vectors, further adding all \c
247+
/// Actions from the resulting merger to \p batchCJA. Do set-union rather than
248+
/// concatenation here to avoid mentioning the same input multiple times.
249+
static bool
250+
mergeBatchInputs(ArrayRef<const Job *> jobs,
251+
llvm::SmallSetVector<const Job *, 16> &inputJobs,
252+
llvm::SmallSetVector<const Action *, 16> &inputActions,
253+
CompileJobAction *batchCJA) {
254+
for (auto const *J : jobs) {
255+
for (auto const *I : J->getInputs()) {
256+
inputJobs.insert(I);
257+
}
258+
auto const *CJA = dyn_cast<CompileJobAction>(&J->getSource());
259+
if (!CJA)
260+
return true;
261+
for (auto const *I : CJA->getInputs()) {
262+
inputActions.insert(I);
263+
}
264+
}
265+
266+
for (auto const *I : inputActions) {
267+
batchCJA->addInput(I);
268+
}
269+
return false;
270+
}
271+
272+
/// Construct a \c BatchJob by merging the constituent \p jobs' CommandOutput,
273+
/// input \c Job and \c Action members. Call through to \c constructInvocation
274+
/// on \p BatchJob, to build the \c InvocationInfo.
275+
std::unique_ptr<Job>
276+
ToolChain::constructBatchJob(ArrayRef<const Job *> jobs,
277+
Compilation &C) const
278+
{
279+
#ifndef NDEBUG
280+
// Verify that the equivalence relation on the jobs also holds pairwise.
281+
for (auto *A : jobs) {
282+
for (auto *B : jobs) {
283+
assert(jobsAreBatchCombinable(C, A, B));
284+
}
285+
}
286+
#endif
287+
if (jobs.size() == 0)
288+
return nullptr;
289+
290+
// Synthetic OutputInfo is a slightly-modified version of the initial
291+
// compilation's OI.
292+
auto OI = C.getOutputInfo();
293+
OI.CompilerMode = OutputInfo::Mode::BatchModeCompile;
294+
295+
auto const *executablePath = jobs[0]->getExecutable();
296+
auto outputType = jobs[0]->getOutput().getPrimaryOutputType();
297+
auto output = makeBatchCommandOutput(jobs, C, outputType);
298+
299+
llvm::SmallSetVector<const Job *, 16> inputJobs;
300+
llvm::SmallSetVector<const Action *, 16> inputActions;
301+
auto *batchCJA = C.createAction<CompileJobAction>(outputType);
302+
if (mergeBatchInputs(jobs, inputJobs, inputActions, batchCJA))
303+
return nullptr;
304+
305+
JobContext context{C, inputJobs.getArrayRef(), inputActions.getArrayRef(),
306+
*output, OI};
307+
auto invocationInfo = constructInvocation(*batchCJA, context);
308+
return llvm::make_unique<BatchJob>(*batchCJA,
309+
inputJobs.takeVector(),
310+
std::move(output),
311+
executablePath,
312+
std::move(invocationInfo.Arguments),
313+
std::move(invocationInfo.ExtraEnvironment),
314+
std::move(invocationInfo.FilelistInfos),
315+
jobs);
316+
}
317+
153318
bool
154319
ToolChain::sanitizerRuntimeLibExists(const ArgList &args,
155320
StringRef sanitizerName) const {

0 commit comments

Comments
 (0)