Skip to content

Commit abfe5e3

Browse files
[Caching] Embed bridging header in binary module correctly when caching
When caching is enabled with include-tree, the bridging header PCH is created from the include tree directly. Setup the rewriter correctly when embedding the bridging header into swift binary module. rdar://125719747
1 parent e871ae4 commit abfe5e3

File tree

6 files changed

+137
-17
lines changed

6 files changed

+137
-17
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ WARNING(could_not_rewrite_bridging_header,none,
4242
ERROR(bridging_header_pch_error,Fatal,
4343
"failed to emit precompiled header '%0' for bridging header '%1'",
4444
(StringRef, StringRef))
45+
ERROR(err_rewrite_bridging_header,none,
46+
"failed to serialize bridging header: '%0'", (StringRef))
4547

4648
ERROR(emit_pcm_error,Fatal,
4749
"failed to emit precompiled module '%0' for module map '%1'",

include/swift/ClangImporter/ClangImporter.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,8 +402,10 @@ class ClangImporter final : public ClangModuleLoader {
402402
getWrapperForModule(const clang::Module *mod,
403403
bool returnOverlayIfPossible = false) const override;
404404

405-
std::string getBridgingHeaderContents(StringRef headerPath, off_t &fileSize,
406-
time_t &fileModTime);
405+
std::string
406+
getBridgingHeaderContents(StringRef headerPath, off_t &fileSize,
407+
time_t &fileModTime,
408+
StringRef pchIncludeTree);
407409

408410
/// Makes a temporary replica of the ClangImporter's CompilerInstance, reads
409411
/// an Objective-C header file into the replica and emits a PCH file of its

lib/ClangImporter/ClangImporter.cpp

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,16 @@
5555
#include "clang/Basic/DiagnosticOptions.h"
5656
#include "clang/Basic/FileEntry.h"
5757
#include "clang/Basic/IdentifierTable.h"
58+
#include "clang/Basic/LangStandard.h"
5859
#include "clang/Basic/Module.h"
5960
#include "clang/Basic/TargetInfo.h"
6061
#include "clang/Basic/Version.h"
6162
#include "clang/CAS/CASOptions.h"
63+
#include "clang/CAS/IncludeTree.h"
6264
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
6365
#include "clang/Frontend/CompilerInvocation.h"
6466
#include "clang/Frontend/FrontendActions.h"
67+
#include "clang/Frontend/IncludeTreePPActions.h"
6568
#include "clang/Frontend/TextDiagnosticPrinter.h"
6669
#include "clang/Frontend/Utils.h"
6770
#include "clang/Index/IndexingAction.h"
@@ -80,7 +83,11 @@
8083
#include "llvm/ADT/STLExtras.h"
8184
#include "llvm/ADT/SmallVector.h"
8285
#include "llvm/ADT/StringExtras.h"
86+
#include "llvm/CAS/CASReference.h"
87+
#include "llvm/CAS/ObjectStore.h"
8388
#include "llvm/Support/CrashRecoveryContext.h"
89+
#include "llvm/Support/Error.h"
90+
#include "llvm/Support/ErrorHandling.h"
8491
#include "llvm/Support/FileCollector.h"
8592
#include "llvm/Support/FileSystem.h"
8693
#include "llvm/Support/Memory.h"
@@ -1766,16 +1773,46 @@ bool ClangImporter::importBridgingHeader(StringRef header, ModuleDecl *adapter,
17661773
std::move(sourceBuffer), implicitImport);
17671774
}
17681775

1769-
std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath,
1770-
off_t &fileSize,
1771-
time_t &fileModTime) {
1776+
static llvm::Expected<llvm::cas::ObjectRef>
1777+
setupIncludeTreeInput(clang::CompilerInvocation &invocation,
1778+
StringRef headerPath, StringRef pchIncludeTree) {
1779+
auto DB = invocation.getCASOpts().getOrCreateDatabases();
1780+
if (!DB)
1781+
return DB.takeError();
1782+
auto CAS = DB->first;
1783+
auto ID = CAS->parseID(pchIncludeTree);
1784+
if (!ID)
1785+
return ID.takeError();
1786+
auto includeTreeRef = CAS->getReference(*ID);
1787+
if (!includeTreeRef)
1788+
return llvm::cas::ObjectStore::createUnknownObjectError(*ID);
1789+
1790+
invocation.getFrontendOpts().Inputs.push_back(clang::FrontendInputFile(
1791+
*includeTreeRef, headerPath, clang::Language::ObjC));
1792+
1793+
return *includeTreeRef;
1794+
}
1795+
1796+
std::string ClangImporter::getBridgingHeaderContents(
1797+
StringRef headerPath, off_t &fileSize, time_t &fileModTime,
1798+
StringRef pchIncludeTree) {
17721799
auto invocation =
17731800
std::make_shared<clang::CompilerInvocation>(*Impl.Invocation);
17741801

17751802
invocation->getFrontendOpts().DisableFree = false;
17761803
invocation->getFrontendOpts().Inputs.clear();
1777-
invocation->getFrontendOpts().Inputs.push_back(
1778-
clang::FrontendInputFile(headerPath, clang::Language::ObjC));
1804+
1805+
std::optional<llvm::cas::ObjectRef> includeTreeRef;
1806+
if (pchIncludeTree.empty())
1807+
invocation->getFrontendOpts().Inputs.push_back(
1808+
clang::FrontendInputFile(headerPath, clang::Language::ObjC));
1809+
else if (auto err =
1810+
setupIncludeTreeInput(*invocation, headerPath, pchIncludeTree)
1811+
.moveInto(includeTreeRef)) {
1812+
Impl.diagnose({}, diag::err_rewrite_bridging_header,
1813+
toString(std::move(err)));
1814+
return "";
1815+
}
17791816

17801817
invocation->getPreprocessorOpts().resetNonModularOptions();
17811818

@@ -1796,18 +1833,36 @@ std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath,
17961833
// write to an in-memory buffer.
17971834
class RewriteIncludesAction : public clang::PreprocessorFrontendAction {
17981835
raw_ostream &OS;
1836+
std::optional<llvm::cas::ObjectRef> includeTreeRef;
17991837

18001838
void ExecuteAction() override {
18011839
clang::CompilerInstance &compiler = getCompilerInstance();
1840+
// If the input is include tree, setup the IncludeTreePPAction.
1841+
if (includeTreeRef) {
1842+
auto IncludeTreeRoot = clang::cas::IncludeTreeRoot::get(
1843+
compiler.getOrCreateObjectStore(), *includeTreeRef);
1844+
if (!IncludeTreeRoot)
1845+
llvm::report_fatal_error(IncludeTreeRoot.takeError());
1846+
auto PPCachedAct =
1847+
clang::createPPActionsFromIncludeTree(*IncludeTreeRoot);
1848+
if (!PPCachedAct)
1849+
llvm::report_fatal_error(PPCachedAct.takeError());
1850+
compiler.getPreprocessor().setPPCachedActions(
1851+
std::move(*PPCachedAct));
1852+
}
1853+
18021854
clang::RewriteIncludesInInput(compiler.getPreprocessor(), &OS,
18031855
compiler.getPreprocessorOutputOpts());
18041856
}
1857+
18051858
public:
1806-
explicit RewriteIncludesAction(raw_ostream &os) : OS(os) {}
1859+
explicit RewriteIncludesAction(
1860+
raw_ostream &os, std::optional<llvm::cas::ObjectRef> includeTree)
1861+
: OS(os), includeTreeRef(includeTree) {}
18071862
};
18081863

18091864
llvm::raw_string_ostream os(result);
1810-
RewriteIncludesAction action(os);
1865+
RewriteIncludesAction action(os, includeTreeRef);
18111866
rewriteInstance.ExecuteAction(action);
18121867
});
18131868

@@ -2528,6 +2583,7 @@ ClangImporter::Implementation::Implementation(
25282583
!ctx.ClangImporterOpts.BridgingHeader.empty()),
25292584
DisableOverlayModules(ctx.ClangImporterOpts.DisableOverlayModules),
25302585
EnableClangSPI(ctx.ClangImporterOpts.EnableClangSPI),
2586+
UseClangIncludeTree(ctx.ClangImporterOpts.UseClangIncludeTree),
25312587
importSymbolicCXXDecls(
25322588
ctx.LangOpts.hasFeature(Feature::ImportSymbolicCXXDecls)),
25332589
IsReadingBridgingPCH(false),

lib/ClangImporter/ImporterImpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
467467
const bool BridgingHeaderExplicitlyRequested;
468468
const bool DisableOverlayModules;
469469
const bool EnableClangSPI;
470+
const bool UseClangIncludeTree;
470471
bool importSymbolicCXXDecls;
471472

472473
bool IsReadingBridgingPCH;

lib/Serialization/Serialization.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,21 +1321,25 @@ void Serializer::writeInputBlock() {
13211321
time_t importedHeaderModTime = 0;
13221322
std::string contents;
13231323
auto importedHeaderPath = Options.ImportedHeader;
1324+
std::string pchIncludeTree;
13241325
// We do not want to serialize the explicitly-specified .pch path if one was
13251326
// provided. Instead we write out the path to the original header source so
13261327
// that clients can consume it.
13271328
if (Options.ExplicitModuleBuild &&
13281329
llvm::sys::path::extension(importedHeaderPath)
1329-
.ends_with(file_types::getExtension(file_types::TY_PCH)))
1330-
importedHeaderPath = clangImporter->getClangInstance()
1331-
.getASTReader()
1332-
->getModuleManager()
1333-
.lookupByFileName(importedHeaderPath)
1334-
->OriginalSourceFileName;
1330+
.ends_with(file_types::getExtension(file_types::TY_PCH))) {
1331+
auto *pch = clangImporter->getClangInstance()
1332+
.getASTReader()
1333+
->getModuleManager()
1334+
.lookupByFileName(importedHeaderPath);
1335+
pchIncludeTree = pch->IncludeTreeID;
1336+
importedHeaderPath = pch->OriginalSourceFileName;
1337+
}
13351338

13361339
if (!importedHeaderPath.empty()) {
13371340
contents = clangImporter->getBridgingHeaderContents(
1338-
importedHeaderPath, importedHeaderSize, importedHeaderModTime);
1341+
importedHeaderPath, importedHeaderSize, importedHeaderModTime,
1342+
pchIncludeTree);
13391343
}
13401344
assert(publicImportSet.count(bridgingHeaderImport));
13411345
ImportedHeader.emit(ScratchRecord,

test/CAS/bridging-header.swift

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
// RUN: %empty-directory(%t)
22
// RUN: split-file %s %t
33

4+
// RUN: %target-swift-frontend -emit-module -o %t/temp.swiftmodule -module-name Test -swift-version 5 \
5+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
6+
// RUN: -Xcc -fmodule-map-file=%t/a.modulemap -Xcc -fmodule-map-file=%t/b.modulemap -import-objc-header %t/Bridging.h \
7+
// RUN: %t/test.swift
8+
49
// RUN: %target-swift-frontend -scan-dependencies -module-name Test -module-cache-path %t/clang-module-cache -O \
510
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
611
// RUN: %t/test.swift -o %t/deps.json -swift-version 5 -cache-compile-job -cas-path %t/cas \
@@ -25,9 +30,57 @@
2530
// CHECK-NEXT: "-Xcc",
2631
// CHECK-NEXT: "llvmcas://{{.*}}"
2732

33+
/// Try build then import from a non-caching compilation.
34+
35+
// RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps.json clang:SwiftShims > %t/shim.cmd
36+
// RUN: %swift_frontend_plain @%t/shim.cmd
37+
// RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps.json clang:B > %t/B.cmd
38+
// RUN: %swift_frontend_plain @%t/B.cmd
39+
// RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps.json clang:A > %t/A.cmd
40+
// RUN: %swift_frontend_plain @%t/A.cmd
41+
42+
// RUN: %{python} %S/Inputs/GenerateExplicitModuleMap.py %t/deps.json > %t/map.json
43+
// RUN: llvm-cas --cas %t/cas --make-blob --data %t/map.json > %t/map.casid
44+
45+
// RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps.json bridgingHeader | tail -n +2 > %t/header.cmd
46+
// RUN: %target-swift-frontend @%t/header.cmd -disable-implicit-swift-modules %t/Bridging.h -O -o %t/bridging.pch
47+
// RUN: %cache-tool -cas-path %t/cas -cache-tool-action print-output-keys -- \
48+
// RUN: %target-swift-frontend @%t/header.cmd -disable-implicit-swift-modules %t/Bridging.h -O -o %t/bridging.pch > %t/keys.json
49+
// RUN: %{python} %S/Inputs/ExtractOutputKey.py %t/keys.json %t/Bridging.h > %t/key
50+
51+
// RUN: %{python} %S/Inputs/BuildCommandExtractor.py %t/deps.json Test > %t/MyApp.cmd
52+
// RUN: echo "\"-disable-implicit-string-processing-module-import\"" >> %t/MyApp.cmd
53+
// RUN: echo "\"-disable-implicit-concurrency-module-import\"" >> %t/MyApp.cmd
54+
// RUN: echo "\"-disable-implicit-swift-modules\"" >> %t/MyApp.cmd
55+
// RUN: echo "\"-import-objc-header\"" >> %t/MyApp.cmd
56+
// RUN: echo "\"%t/bridging.pch\"" >> %t/MyApp.cmd
57+
// RUN: echo "\"-bridging-header-pch-key\"" >> %t/MyApp.cmd
58+
// RUN: echo "\"@%t/key\"" >> %t/MyApp.cmd
59+
// RUN: echo "\"-explicit-swift-module-map-file\"" >> %t/MyApp.cmd
60+
// RUN: echo "\"@%t/map.casid\"" >> %t/MyApp.cmd
61+
62+
// RUN: %target-swift-frontend -cache-compile-job -module-name Test -O -cas-path %t/cas @%t/MyApp.cmd %t/test.swift \
63+
// RUN: -emit-module -o %t/Test.swiftmodule
64+
65+
// RUN: %target-swift-frontend -typecheck -module-name User -swift-version 5 \
66+
// RUN: -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import \
67+
// RUN: -Xcc -fmodule-map-file=%t/a.modulemap -Xcc -fmodule-map-file=%t/b.modulemap \
68+
// RUN: -I %t %t/user.swift
69+
2870
//--- test.swift
2971
import B
30-
public func test() {}
72+
public func test() {
73+
b()
74+
}
75+
public class TestB: B {}
76+
77+
//--- user.swift
78+
import Test
79+
80+
func user() {
81+
var b: TestB
82+
test()
83+
}
3184

3285
//--- Bridging.h
3386
#include "Foo.h"
@@ -40,6 +93,8 @@ public func test() {}
4093

4194
//--- b.h
4295
void b(void);
96+
@interface B
97+
@end
4398
4499
//--- a.modulemap
45100
module A {

0 commit comments

Comments
 (0)