Skip to content

Commit 114e63a

Browse files
committed
[Bridging PCH] Auto-generate and use temporary bridging PCHs from driver.
1 parent 80c3b2c commit 114e63a

File tree

11 files changed

+163
-13
lines changed

11 files changed

+163
-13
lines changed

include/swift/Driver/Action.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ class Action {
4848
REPLJob,
4949
LinkJob,
5050
GenerateDSYMJob,
51+
GeneratePCHJob,
5152

5253
JobFirst=CompileJob,
53-
JobLast=GenerateDSYMJob
54+
JobLast=GeneratePCHJob
5455
};
5556

5657
static const char *getClassName(ActionClass AC);
@@ -268,6 +269,17 @@ class GenerateDSYMJobAction : public JobAction {
268269
}
269270
};
270271

272+
class GeneratePCHJobAction : public JobAction {
273+
virtual void anchor();
274+
public:
275+
explicit GeneratePCHJobAction(Action *Input)
276+
: JobAction(Action::GeneratePCHJob, Input, types::TY_PCH) {}
277+
278+
static bool classof(const Action *A) {
279+
return A->getKind() == Action::GeneratePCHJob;
280+
}
281+
};
282+
271283
class LinkJobAction : public JobAction {
272284
virtual void anchor();
273285
LinkKind Kind;

include/swift/Driver/ToolChain.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ class ToolChain {
122122
constructInvocation(const GenerateDSYMJobAction &job,
123123
const JobContext &context) const;
124124
virtual InvocationInfo
125+
constructInvocation(const GeneratePCHJobAction &job,
126+
const JobContext &context) const;
127+
virtual InvocationInfo
125128
constructInvocation(const AutolinkExtractJobAction &job,
126129
const JobContext &context) const;
127130
virtual InvocationInfo

include/swift/Option/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,10 @@ def disable_swift_bridge_attr : Flag<["-"], "disable-swift-bridge-attr">,
237237
Flags<[FrontendOption, HelpHidden]>,
238238
HelpText<"Disable using the swift bridge attribute">;
239239

240+
def disable_bridging_pch : Flag<["-"], "disable-bridging-pch">,
241+
Flags<[HelpHidden]>,
242+
HelpText<"Disable automatic generation of bridging PCH files">;
243+
240244
// Diagnostic control options
241245
def suppress_warnings : Flag<["-"], "suppress-warnings">,
242246
Flags<[FrontendOption]>,

lib/Driver/Action.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const char *Action::getClassName(ActionClass AC) {
3636
case REPLJob: return "repl";
3737
case LinkJob: return "link";
3838
case GenerateDSYMJob: return "generate-dSYM";
39+
case GeneratePCHJob: return "generate-pch";
3940
}
4041

4142
llvm_unreachable("invalid class");
@@ -62,3 +63,5 @@ void REPLJobAction::anchor() {}
6263
void LinkJobAction::anchor() {}
6364

6465
void GenerateDSYMJobAction::anchor() {}
66+
67+
void GeneratePCHJobAction::anchor() {}

lib/Driver/Compilation.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ static void populateInputInfoMap(InputInfoMap &inputs,
115115
const PerformJobsState &endState) {
116116
for (auto &entry : endState.UnfinishedCommands) {
117117
for (auto *action : entry.first->getSource().getInputs()) {
118-
auto inputFile = cast<InputAction>(action);
118+
auto inputFile = dyn_cast<InputAction>(action);
119+
if (!inputFile)
120+
continue;
119121

120122
CompileJobAction::InputInfo info;
121123
info.previousModTime = entry.first->getInputModTime();
@@ -132,7 +134,9 @@ static void populateInputInfoMap(InputInfoMap &inputs,
132134
continue;
133135

134136
for (auto *action : compileAction->getInputs()) {
135-
auto inputFile = cast<InputAction>(action);
137+
auto inputFile = dyn_cast<InputAction>(action);
138+
if (!inputFile)
139+
continue;
136140

137141
CompileJobAction::InputInfo info;
138142
info.previousModTime = entry->getInputModTime();

lib/Driver/Driver.cpp

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,6 +1270,24 @@ void Driver::buildActions(const ToolChain &TC,
12701270
switch (OI.CompilerMode) {
12711271
case OutputInfo::Mode::StandardCompile:
12721272
case OutputInfo::Mode::UpdateCode: {
1273+
1274+
// If the user is importing a textual (.h) bridging header and we're in
1275+
// standard-compile (non-WMO) mode, we take the opportunity to precompile
1276+
// the header into a temporary PCH, and replace the import argument with the
1277+
// PCH in the subsequent frontend jobs.
1278+
JobAction *PCH = nullptr;
1279+
if (!Args.hasArg(options::OPT_disable_bridging_pch)) {
1280+
if (Arg *A = Args.getLastArg(options::OPT_import_objc_header)) {
1281+
StringRef Value = A->getValue();
1282+
auto Ty = TC.lookupTypeForExtension(llvm::sys::path::extension(Value));
1283+
if (Ty == types::TY_ObjCHeader) {
1284+
std::unique_ptr<Action> HeaderInput(new InputAction(*A, Ty));
1285+
PCH = new GeneratePCHJobAction(HeaderInput.release());
1286+
Actions.push_back(PCH);
1287+
}
1288+
}
1289+
}
1290+
12731291
for (const InputPair &Input : Inputs) {
12741292
types::ID InputType = Input.first;
12751293
const Arg *InputArg = Input.second;
@@ -1301,6 +1319,21 @@ void Driver::buildActions(const ToolChain &TC,
13011319
previousBuildState));
13021320
AllModuleInputs.push_back(Current.get());
13031321
}
1322+
if (PCH) {
1323+
// FIXME: When we have a PCH job, it's officially owned by the Actions
1324+
// array; but it's also a secondary input to each of the current
1325+
// JobActions, which means that we need to flip the "owns inputs" bit
1326+
// on the JobActions so they don't try to free it. That in turn means
1327+
// we need to transfer ownership of all the JobActions' existing
1328+
// inputs to the Actions array, since the JobActions either own or
1329+
// don't own _all_ of their inputs. Ownership can't vary
1330+
// input-by-input.
1331+
auto *job = cast<JobAction>(Current.get());
1332+
auto inputs = job->getInputs();
1333+
Actions.append(inputs.begin(), inputs.end());
1334+
job->setOwnsInputs(false);
1335+
job->addInput(PCH);
1336+
}
13041337
AllLinkerInputs.push_back(Current.release());
13051338
break;
13061339
}
@@ -1554,6 +1587,12 @@ void Driver::buildJobs(const ActionList &Actions, const OutputInfo &OI,
15541587
unsigned NumOutputs = 0;
15551588
for (const Action *A : Actions) {
15561589
types::ID Type = A->getType();
1590+
1591+
// Skip any GeneratePCHJobActions or InputActions incidentally stored in
1592+
// Actions (for ownership), as a result of PCH-generation.
1593+
if (isa<GeneratePCHJobAction>(A) || isa<InputAction>(A))
1594+
continue;
1595+
15571596
// Only increment NumOutputs if this is an output which must have its
15581597
// path specified using -o.
15591598
// (Module outputs can be specified using -module-output-path, or will
@@ -1580,8 +1619,10 @@ void Driver::buildJobs(const ActionList &Actions, const OutputInfo &OI,
15801619
}
15811620

15821621
for (const Action *A : Actions) {
1583-
(void)buildJobsForAction(C, cast<JobAction>(A), OI, OFM, TC,
1584-
/*TopLevel*/true, JobCache);
1622+
if (auto *JA = dyn_cast<JobAction>(A)) {
1623+
(void)buildJobsForAction(C, JA, OI, OFM, TC,
1624+
/*TopLevel*/true, JobCache);
1625+
}
15851626
}
15861627
}
15871628

@@ -1640,6 +1681,13 @@ static StringRef getOutputFilename(Compilation &C,
16401681
return Buffer.str();
16411682
}
16421683

1684+
// FIXME: Treat GeneratePCHJobAction as non-top-level (to get tempfile and not
1685+
// use the -o arg) even though, based on ownership considerations within the
1686+
// driver, it is stored as a "top level" JobAction.
1687+
if (isa<GeneratePCHJobAction>(JA)) {
1688+
AtTopLevel = false;
1689+
}
1690+
16431691
// We don't have an output from an Action-specific command line option,
16441692
// so figure one out using the defaults.
16451693
if (AtTopLevel) {
@@ -1660,8 +1708,8 @@ static StringRef getOutputFilename(Compilation &C,
16601708

16611709
// We don't yet have a name, assign one.
16621710
if (!AtTopLevel) {
1663-
// We should output to a temporary file, since we're not at
1664-
// the top level.
1711+
// We should output to a temporary file, since we're not at the top level
1712+
// (or are generating a bridging PCH, which is currently always a temp).
16651713
StringRef Stem = llvm::sys::path::stem(BaseName);
16661714
StringRef Suffix = types::getTypeTempSuffix(JA->getType());
16671715
std::error_code EC =

lib/Driver/ToolChain.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ ToolChain::constructJob(const JobAction &JA,
8383
CASE(ModuleWrapJob)
8484
CASE(LinkJob)
8585
CASE(GenerateDSYMJob)
86+
CASE(GeneratePCHJob)
8687
CASE(AutolinkExtractJob)
8788
CASE(REPLJob)
8889
#undef CASE

lib/Driver/ToolChains.cpp

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ static void addCommonFrontendArgs(const ToolChain &TC,
133133
inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension);
134134
inputArgs.AddLastArg(arguments, options::OPT_enable_testing);
135135
inputArgs.AddLastArg(arguments, options::OPT_g_Group);
136-
inputArgs.AddLastArg(arguments, options::OPT_import_objc_header);
137136
inputArgs.AddLastArg(arguments, options::OPT_import_underlying_module);
138137
inputArgs.AddLastArg(arguments, options::OPT_module_cache_path);
139138
inputArgs.AddLastArg(arguments, options::OPT_module_link_name);
@@ -258,9 +257,6 @@ ToolChain::constructInvocation(const CompileJobAction &job,
258257

259258
Arguments.push_back(FrontendModeOption);
260259

261-
assert(context.Inputs.empty() &&
262-
"The Swift frontend does not expect to be fed any input Jobs!");
263-
264260
// Add input arguments.
265261
switch (context.OI.CompilerMode) {
266262
case OutputInfo::Mode::StandardCompile:
@@ -318,6 +314,23 @@ ToolChain::constructInvocation(const CompileJobAction &job,
318314
addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
319315
Arguments);
320316

317+
// Pass along an -import-objc-header arg, replacing the argument with the name
318+
// of any input PCH to the current action if one is present.
319+
if (context.Args.hasArgNoClaim(options::OPT_import_objc_header)) {
320+
bool ForwardAsIs = true;
321+
for (auto *IJ : context.Inputs) {
322+
if (!IJ->getOutput().getAnyOutputForType(types::TY_PCH).empty()) {
323+
Arguments.push_back("-import-objc-header");
324+
addInputsOfType(Arguments, context.Inputs, types::TY_PCH);
325+
ForwardAsIs = false;
326+
break;
327+
}
328+
}
329+
if (ForwardAsIs) {
330+
context.Args.AddLastArg(Arguments, options::OPT_import_objc_header);
331+
}
332+
}
333+
321334
// Pass the optimization level down to the frontend.
322335
context.Args.AddLastArg(Arguments, options::OPT_O_Group);
323336

@@ -426,6 +439,7 @@ ToolChain::constructInvocation(const InterpretJobAction &job,
426439

427440
addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
428441
Arguments);
442+
context.Args.AddLastArg(Arguments, options::OPT_import_objc_header);
429443

430444
// Pass the optimization level down to the frontend.
431445
context.Args.AddLastArg(Arguments, options::OPT_O_Group);
@@ -618,6 +632,7 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job,
618632

619633
addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
620634
Arguments);
635+
context.Args.AddLastArg(Arguments, options::OPT_import_objc_header);
621636

622637
Arguments.push_back("-module-name");
623638
Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));
@@ -687,6 +702,7 @@ ToolChain::constructInvocation(const REPLJobAction &job,
687702
ArgStringList FrontendArgs;
688703
addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
689704
FrontendArgs);
705+
context.Args.AddLastArg(FrontendArgs, options::OPT_import_objc_header);
690706
context.Args.AddAllArgs(FrontendArgs, options::OPT_l, options::OPT_framework,
691707
options::OPT_L);
692708

@@ -731,6 +747,30 @@ ToolChain::constructInvocation(const GenerateDSYMJobAction &job,
731747
return {"dsymutil", Arguments};
732748
}
733749

750+
ToolChain::InvocationInfo
751+
ToolChain::constructInvocation(const GeneratePCHJobAction &job,
752+
const JobContext &context) const {
753+
assert(context.Inputs.empty());
754+
assert(context.InputActions.size() == 1);
755+
assert(context.Output.getPrimaryOutputType() == types::TY_PCH);
756+
757+
ArgStringList Arguments;
758+
759+
Arguments.push_back("-frontend");
760+
761+
addCommonFrontendArgs(*this, context.OI, context.Output, context.Args,
762+
Arguments);
763+
764+
addInputsOfType(Arguments, context.InputActions, types::TY_ObjCHeader);
765+
766+
Arguments.push_back("-emit-pch");
767+
Arguments.push_back("-o");
768+
Arguments.push_back(
769+
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
770+
771+
return {SWIFT_EXECUTABLE_NAME, Arguments};
772+
}
773+
734774
ToolChain::InvocationInfo
735775
ToolChain::constructInvocation(const AutolinkExtractJobAction &job,
736776
const JobContext &context) const {

test/ClangImporter/pch-bridging-header.swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,24 @@
1-
// RUN: rm -rf %t && mkdir -p %t
1+
// REQUIRES: objc_interop
2+
// RUN: rm -rf %t && mkdir -p %t/tmp
3+
4+
// First test the explicit frontend-based bridging PCH generation and use works
25
// RUN: %target-swift-frontend -emit-pch -o %t/sdk-bridging-header.pch %S/Inputs/sdk-bridging-header.h
36
// RUN: %target-swift-frontend -parse -verify %s -import-objc-header %t/sdk-bridging-header.pch
4-
// REQUIRES: objc_interop
7+
8+
// Now test the driver-automated version is inert when disabled
9+
// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -disable-bridging-pch -parse -save-temps %s -import-objc-header %S/Inputs/sdk-bridging-header.h
10+
// RUN: not ls %t/tmp/*.pch >/dev/null 2>&1
11+
12+
// Test the driver-automated version works when not-disabled
13+
// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -parse -save-temps %s -import-objc-header %S/Inputs/sdk-bridging-header.h
14+
// RUN: ls %t/tmp/*.pch >/dev/null 2>&1
15+
// RUN: llvm-objdump -raw-clang-ast %t/tmp/*.pch | llvm-bcanalyzer -dump | %FileCheck %s
16+
// CHECK: ORIGINAL_FILE{{.*}}Inputs/sdk-bridging-header.h
17+
18+
// Test the driver-automated version deletes its PCH file when done
19+
// RUN: rm %t/tmp/*.pch
20+
// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -parse %s -import-objc-header %S/Inputs/sdk-bridging-header.h
21+
// RUN: not ls %t/tmp/*.pch >/dev/null 2>&1
522

623
import Foundation
724

test/Driver/Inputs/bridging-header.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
extern int x;

test/Driver/bridging-pch.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %swiftc_driver -typecheck -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=YESPCHACT
2+
// YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", objc-header
3+
// YESPCHACT: 1: generate-pch, {0}, pch
4+
// YESPCHACT: 2: input, "{{.*}}bridging-pch.swift", swift
5+
// YESPCHACT: 3: compile, {2, 1}, none
6+
7+
// RUN: %swiftc_driver -typecheck -disable-bridging-pch -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=NOPCHACT
8+
// NOPCHACT: 0: input, "{{.*}}bridging-pch.swift", swift
9+
// NOPCHACT: 1: compile, {0}, none
10+
11+
// RUN: %swiftc_driver -typecheck -driver-print-jobs -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=YESPCHJOB
12+
// YESPCHJOB: {{.*}}swift -frontend {{.*}} -emit-pch -o {{.*}}bridging-header-{{.*}}.pch
13+
// YESPCHJOB: {{.*}}swift -frontend {{.*}} -import-objc-header {{.*}}bridging-header-{{.*}}.pch
14+
15+
// RUN: %swiftc_driver -typecheck -disable-bridging-pch -driver-print-jobs -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=NOPCHJOB
16+
// NOPCHJOB: {{.*}}swift -frontend {{.*}} -import-objc-header {{.*}}Inputs/bridging-header.h
17+

0 commit comments

Comments
 (0)