Skip to content

Commit cb6fa8d

Browse files
authored
Merge pull request #21278 from jrose-apple/build-parseable-interface-to-module
Add frontend mode -build-module-from-parseable-interface
2 parents d5f5ba6 + 37708ed commit cb6fa8d

File tree

9 files changed

+132
-32
lines changed

9 files changed

+132
-32
lines changed

include/swift/Frontend/FrontendOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ class FrontendOptions {
126126
EmitModuleOnly, ///< Emit module only
127127
MergeModules, ///< Merge modules only
128128

129+
/// Build from a swiftinterface, as close to `import` as possible
130+
BuildModuleFromParseableInterface,
131+
129132
EmitSIBGen, ///< Emit serialized AST + raw SIL
130133
EmitSIB, ///< Emit serialized AST + canonical SIL
131134

include/swift/Frontend/ParseableInterfaceSupport.h

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,13 @@ class ParseableInterfaceModuleLoader : public SerializedModuleLoaderBase {
6969

7070
std::string CacheDir;
7171

72-
void
73-
configureSubInvocationAndOutputPaths(CompilerInvocation &SubInvocation,
74-
Identifier ModuleName, StringRef InPath,
75-
llvm::SmallString<128> &OutPath);
72+
/// Wire up the SubInvocation's InputsAndOutputs to contain both input and
73+
/// output filenames.
74+
///
75+
/// This is a method rather than a helper function in the implementation file
76+
/// because it accesses non-public bits of FrontendInputsAndOutputs.
77+
static void configureSubInvocationInputsAndOutputs(
78+
CompilerInvocation &SubInvocation, StringRef InPath, StringRef OutPath);
7679

7780
std::error_code
7881
openModuleFiles(AccessPathElem ModuleID, StringRef DirName,
@@ -89,6 +92,17 @@ class ParseableInterfaceModuleLoader : public SerializedModuleLoaderBase {
8992
return std::unique_ptr<ParseableInterfaceModuleLoader>(
9093
new ParseableInterfaceModuleLoader(ctx, cacheDir, tracker, loadMode));
9194
}
95+
96+
/// Unconditionally build \p InPath (a swiftinterface file) to \p OutPath (as
97+
/// a swiftmodule file).
98+
///
99+
/// A simplified version of the core logic in #openModuleFiles, mostly for
100+
/// testing purposes.
101+
static bool buildSwiftModuleFromSwiftInterface(ASTContext &Ctx,
102+
StringRef CacheDir,
103+
StringRef ModuleName,
104+
StringRef InPath,
105+
StringRef OutPath);
92106
};
93107

94108

include/swift/Option/FrontendOptions.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,11 @@ def dump_interface_hash : Flag<["-"], "dump-interface-hash">,
513513
HelpText<"Parse input file(s) and dump interface token hash(es)">,
514514
ModeOpt;
515515

516+
def build_module_from_parseable_interface :
517+
Flag<["-"], "build-module-from-parseable-interface">,
518+
HelpText<"Treat the (single) input as a swiftinterface and produce a module">,
519+
ModeOpt;
520+
516521
def enable_resilience_bypass : Flag<["-"], "enable-resilience-bypass">,
517522
HelpText<"Completely bypass resilience when accessing types in resilient frameworks">;
518523

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,8 @@ ArgsToFrontendOptionsConverter::determineRequestedAction(const ArgList &args) {
370370
return FrontendOptions::ActionType::REPL;
371371
if (Opt.matches(OPT_interpret))
372372
return FrontendOptions::ActionType::Immediate;
373+
if (Opt.matches(OPT_build_module_from_parseable_interface))
374+
return FrontendOptions::ActionType::BuildModuleFromParseableInterface;
373375

374376
llvm_unreachable("Unhandled mode option");
375377
}

lib/Frontend/FrontendOptions.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ bool FrontendOptions::needsProperModuleName(ActionType action) {
4848
case ActionType::EmitSIB:
4949
case ActionType::EmitModuleOnly:
5050
case ActionType::MergeModules:
51+
case ActionType::BuildModuleFromParseableInterface:
5152
return true;
5253
case ActionType::Immediate:
5354
case ActionType::REPL:
@@ -83,6 +84,7 @@ bool FrontendOptions::isActionImmediate(ActionType action) {
8384
case ActionType::EmitSIB:
8485
case ActionType::EmitModuleOnly:
8586
case ActionType::MergeModules:
87+
case ActionType::BuildModuleFromParseableInterface:
8688
return false;
8789
case ActionType::Immediate:
8890
case ActionType::REPL:
@@ -170,6 +172,7 @@ FrontendOptions::formatForPrincipalOutputFileForAction(ActionType action) {
170172

171173
case ActionType::MergeModules:
172174
case ActionType::EmitModuleOnly:
175+
case ActionType::BuildModuleFromParseableInterface:
173176
return TY_SwiftModuleFile;
174177

175178
case ActionType::Immediate:
@@ -207,6 +210,7 @@ bool FrontendOptions::canActionEmitDependencies(ActionType action) {
207210
case ActionType::DumpScopeMaps:
208211
case ActionType::DumpTypeRefinementContexts:
209212
case ActionType::DumpTypeInfo:
213+
case ActionType::BuildModuleFromParseableInterface:
210214
case ActionType::Immediate:
211215
case ActionType::REPL:
212216
return false;
@@ -242,6 +246,7 @@ bool FrontendOptions::canActionEmitReferenceDependencies(ActionType action) {
242246
case ActionType::DumpScopeMaps:
243247
case ActionType::DumpTypeRefinementContexts:
244248
case ActionType::DumpTypeInfo:
249+
case ActionType::BuildModuleFromParseableInterface:
245250
case ActionType::Immediate:
246251
case ActionType::REPL:
247252
return false;
@@ -277,6 +282,7 @@ bool FrontendOptions::canActionEmitObjCHeader(ActionType action) {
277282
case ActionType::DumpScopeMaps:
278283
case ActionType::DumpTypeRefinementContexts:
279284
case ActionType::DumpTypeInfo:
285+
case ActionType::BuildModuleFromParseableInterface:
280286
case ActionType::Immediate:
281287
case ActionType::REPL:
282288
return false;
@@ -309,6 +315,7 @@ bool FrontendOptions::canActionEmitLoadedModuleTrace(ActionType action) {
309315
case ActionType::DumpScopeMaps:
310316
case ActionType::DumpTypeRefinementContexts:
311317
case ActionType::DumpTypeInfo:
318+
case ActionType::BuildModuleFromParseableInterface:
312319
case ActionType::Immediate:
313320
case ActionType::REPL:
314321
return false;
@@ -347,6 +354,7 @@ bool FrontendOptions::canActionEmitModule(ActionType action) {
347354
case ActionType::DumpTypeRefinementContexts:
348355
case ActionType::DumpTypeInfo:
349356
case ActionType::EmitSILGen:
357+
case ActionType::BuildModuleFromParseableInterface:
350358
case ActionType::Immediate:
351359
case ActionType::REPL:
352360
return false;
@@ -379,15 +387,16 @@ bool FrontendOptions::canActionEmitInterface(ActionType action) {
379387
case ActionType::DumpAST:
380388
case ActionType::EmitSyntax:
381389
case ActionType::PrintAST:
390+
case ActionType::EmitImportedModules:
382391
case ActionType::EmitPCH:
383392
case ActionType::DumpScopeMaps:
384393
case ActionType::DumpTypeRefinementContexts:
385394
case ActionType::DumpTypeInfo:
386395
case ActionType::EmitSILGen:
387396
case ActionType::EmitSIBGen:
397+
case ActionType::BuildModuleFromParseableInterface:
388398
case ActionType::Immediate:
389399
case ActionType::REPL:
390-
case ActionType::EmitImportedModules:
391400
return false;
392401
case ActionType::Typecheck:
393402
case ActionType::MergeModules:
@@ -427,6 +436,7 @@ bool FrontendOptions::doesActionProduceOutput(ActionType action) {
427436
case ActionType::EmitObject:
428437
case ActionType::EmitImportedModules:
429438
case ActionType::MergeModules:
439+
case ActionType::BuildModuleFromParseableInterface:
430440
case ActionType::DumpTypeInfo:
431441
return true;
432442

@@ -446,6 +456,7 @@ bool FrontendOptions::doesActionProduceTextualOutput(ActionType action) {
446456
case ActionType::EmitSIB:
447457
case ActionType::MergeModules:
448458
case ActionType::EmitModuleOnly:
459+
case ActionType::BuildModuleFromParseableInterface:
449460
case ActionType::EmitBC:
450461
case ActionType::EmitObject:
451462
case ActionType::Immediate:
@@ -488,6 +499,7 @@ bool FrontendOptions::doesActionGenerateSIL(ActionType action) {
488499
case ActionType::DumpTypeRefinementContexts:
489500
case ActionType::EmitImportedModules:
490501
case ActionType::EmitPCH:
502+
case ActionType::BuildModuleFromParseableInterface:
491503
return false;
492504
case ActionType::EmitSILGen:
493505
case ActionType::EmitSIBGen:

lib/Frontend/ParseableInterfaceSupport.cpp

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ getBufferOfDependency(clang::vfs::FileSystem &FS,
107107
/// with dead entries -- when other factors change, such as the contents of
108108
/// the .swiftinterface input or its dependencies.
109109
static std::string getCacheHash(ASTContext &Ctx,
110-
CompilerInvocation &SubInvocation,
110+
const CompilerInvocation &SubInvocation,
111111
StringRef InPath) {
112112
// Start with the compiler version (which will be either tag names or revs).
113113
std::string vers = swift::version::getSwiftFullVersion(
@@ -128,16 +128,14 @@ static std::string getCacheHash(ASTContext &Ctx,
128128
return llvm::APInt(64, H).toString(36, /*Signed=*/false);
129129
}
130130

131-
void
132-
ParseableInterfaceModuleLoader::configureSubInvocationAndOutputPaths(
133-
CompilerInvocation &SubInvocation,
134-
Identifier ModuleName,
135-
StringRef InPath,
136-
llvm::SmallString<128> &OutPath) {
137-
131+
static CompilerInvocation
132+
createInvocationForBuildingFromInterface(ASTContext &Ctx, StringRef ModuleName,
133+
StringRef CacheDir) {
138134
auto &SearchPathOpts = Ctx.SearchPathOpts;
139135
auto &LangOpts = Ctx.LangOpts;
140136

137+
CompilerInvocation SubInvocation;
138+
141139
// Start with a SubInvocation that copies various state from our
142140
// invoking ASTContext.
143141
SubInvocation.setImportSearchPaths(SearchPathOpts.ImportSearchPaths);
@@ -147,7 +145,7 @@ ParseableInterfaceModuleLoader::configureSubInvocationAndOutputPaths(
147145
SubInvocation.setRuntimeResourcePath(SearchPathOpts.RuntimeResourcePath);
148146
SubInvocation.setTargetTriple(LangOpts.Target);
149147
SubInvocation.setClangModuleCachePath(CacheDir);
150-
SubInvocation.setModuleName(ModuleName.str());
148+
SubInvocation.setModuleName(ModuleName);
151149

152150
// Inhibit warnings from the SubInvocation since we are assuming the user
153151
// is not in a position to fix them.
@@ -161,24 +159,35 @@ ParseableInterfaceModuleLoader::configureSubInvocationAndOutputPaths(
161159
// modules that don't import Foundation.
162160
SubInvocation.getLangOptions().EnableObjCAttrRequiresFoundation = false;
163161

164-
// Calculate an output filename that includes a hash of relevant key data, and
165-
// wire up the SubInvocation's InputsAndOutputs to contain both input and
166-
// output filenames.
167-
OutPath = CacheDir;
168-
llvm::sys::path::append(OutPath, ModuleName.str());
162+
return SubInvocation;
163+
}
164+
165+
/// Calculate an output filename in \p SubInvocation's cache path that includes
166+
/// a hash of relevant key data.
167+
static void computeCachedOutputPath(ASTContext &Ctx,
168+
const CompilerInvocation &SubInvocation,
169+
StringRef InPath,
170+
llvm::SmallString<128> &OutPath) {
171+
OutPath = SubInvocation.getClangModuleCachePath();
172+
llvm::sys::path::append(OutPath, SubInvocation.getModuleName());
169173
OutPath.append("-");
170174
OutPath.append(getCacheHash(Ctx, SubInvocation, InPath));
171175
OutPath.append(".");
172176
auto OutExt = file_types::getExtension(file_types::TY_SwiftModuleFile);
173177
OutPath.append(OutExt);
178+
}
174179

180+
void ParseableInterfaceModuleLoader::configureSubInvocationInputsAndOutputs(
181+
CompilerInvocation &SubInvocation, StringRef InPath, StringRef OutPath) {
175182
auto &SubFEOpts = SubInvocation.getFrontendOptions();
176183
SubFEOpts.RequestedAction = FrontendOptions::ActionType::EmitModuleOnly;
177184
SubFEOpts.EnableParseableModuleInterface = true;
178185
SubFEOpts.InputsAndOutputs.addPrimaryInputFile(InPath);
179186
SupplementaryOutputPaths SOPs;
180187
SOPs.ModuleOutputPath = OutPath.str();
181-
StringRef MainOut = "/dev/null";
188+
189+
// Pick a primary output path that will cause problems to use.
190+
StringRef MainOut = "/<unused>";
182191
SubFEOpts.InputsAndOutputs.setMainAndSupplementaryOutputs({MainOut}, {SOPs});
183192
}
184193

@@ -258,6 +267,9 @@ collectDepsForSerialization(clang::vfs::FileSystem &FS,
258267
uint64_t Hash = xxHash64(DepBuf->getBuffer());
259268
Deps.push_back(FileDependency{Size, Hash, DepName});
260269

270+
if (ModuleCachePath.empty())
271+
continue;
272+
261273
// If Dep is itself a .swiftmodule in the cache dir, pull out its deps
262274
// and include them in our own, so we have a single-file view of
263275
// transitive deps: removes redundancies, and avoids opening and reading
@@ -290,11 +302,21 @@ collectDepsForSerialization(clang::vfs::FileSystem &FS,
290302

291303
static bool buildSwiftModuleFromSwiftInterface(
292304
clang::vfs::FileSystem &FS, DiagnosticEngine &Diags, SourceLoc DiagLoc,
293-
CompilerInvocation &SubInvocation, StringRef InPath, StringRef OutPath,
294-
StringRef ModuleCachePath, DependencyTracker *OuterTracker) {
305+
CompilerInvocation &SubInvocation, StringRef ModuleCachePath,
306+
DependencyTracker *OuterTracker) {
295307
bool SubError = false;
296308
bool RunSuccess = llvm::CrashRecoveryContext().RunSafelyOnThread([&] {
297-
(void)llvm::sys::fs::create_directory(ModuleCachePath);
309+
// Note that we don't assume ModuleCachePath is the same as the Clang
310+
// module cache path at this point.
311+
if (!ModuleCachePath.empty())
312+
(void)llvm::sys::fs::create_directory(ModuleCachePath);
313+
314+
FrontendOptions &FEOpts = SubInvocation.getFrontendOptions();
315+
const auto &InputInfo = FEOpts.InputsAndOutputs.firstInput();
316+
StringRef InPath = InputInfo.file();
317+
const auto &OutputInfo =
318+
InputInfo.getPrimarySpecificPaths().SupplementaryOutputs;
319+
StringRef OutPath = OutputInfo.ModuleOutputPath;
298320

299321
llvm::BumpPtrAllocator SubArgsAlloc;
300322
llvm::StringSaver SubArgSaver(SubArgsAlloc);
@@ -343,6 +365,7 @@ static bool buildSwiftModuleFromSwiftInterface(
343365
LLVM_DEBUG(llvm::dbgs() << "Setting up instance to compile "
344366
<< InPath << " to " << OutPath << "\n");
345367
CompilerInstance SubInstance;
368+
SubInstance.getSourceMgr().setFileSystem(&FS);
346369

347370
ForwardingDiagnosticConsumer FDC(Diags);
348371
SubInstance.addDiagnosticConsumer(&FDC);
@@ -372,7 +395,6 @@ static bool buildSwiftModuleFromSwiftInterface(
372395

373396
// Setup the callbacks for serialization, which can occur during the
374397
// optimization pipeline.
375-
FrontendOptions &FEOpts = SubInvocation.getFrontendOptions();
376398
SerializationOptions SerializationOpts;
377399
std::string OutPathStr = OutPath;
378400
SerializationOpts.OutputPath = OutPathStr.c_str();
@@ -456,15 +478,16 @@ std::error_code ParseableInterfaceModuleLoader::openModuleFiles(
456478

457479
// Set up a _potential_ sub-invocation to consume the .swiftinterface and emit
458480
// the .swiftmodule.
459-
CompilerInvocation SubInvocation;
460-
configureSubInvocationAndOutputPaths(SubInvocation, ModuleID.first, InPath,
461-
OutPath);
481+
CompilerInvocation SubInvocation =
482+
createInvocationForBuildingFromInterface(Ctx, ModuleID.first.str(), CacheDir);
483+
computeCachedOutputPath(Ctx, SubInvocation, InPath, OutPath);
484+
configureSubInvocationInputsAndOutputs(SubInvocation, InPath, OutPath);
462485

463486
// Evaluate if we need to run this sub-invocation, and if so run it.
464487
if (!swiftModuleIsUpToDate(FS, ModuleID, OutPath, Diags, dependencyTracker)) {
465-
if (buildSwiftModuleFromSwiftInterface(FS, Diags, ModuleID.second,
466-
SubInvocation, InPath, OutPath,
467-
CacheDir, dependencyTracker))
488+
if (::buildSwiftModuleFromSwiftInterface(FS, Diags, ModuleID.second,
489+
SubInvocation, CacheDir,
490+
dependencyTracker))
468491
return std::make_error_code(std::errc::invalid_argument);
469492
}
470493

@@ -484,6 +507,21 @@ std::error_code ParseableInterfaceModuleLoader::openModuleFiles(
484507
return ErrorCode;
485508
}
486509

510+
bool
511+
ParseableInterfaceModuleLoader::buildSwiftModuleFromSwiftInterface(
512+
ASTContext &Ctx, StringRef CacheDir, StringRef ModuleName,
513+
StringRef InPath, StringRef OutPath) {
514+
CompilerInvocation SubInvocation =
515+
createInvocationForBuildingFromInterface(Ctx, ModuleName, CacheDir);
516+
configureSubInvocationInputsAndOutputs(SubInvocation, InPath, OutPath);
517+
518+
auto &FS = *Ctx.SourceMgr.getFileSystem();
519+
auto &Diags = Ctx.Diags;
520+
return ::buildSwiftModuleFromSwiftInterface(FS, Diags, /*DiagLoc*/SourceLoc(),
521+
SubInvocation, /*CachePath*/"",
522+
/*OuterTracker*/nullptr);
523+
}
524+
487525
/// Diagnose any scoped imports in \p imports, i.e. those with a non-empty
488526
/// access path. These are not yet supported by parseable interfaces, since the
489527
/// information about the declaration kind is not preserved through the binary

lib/FrontendTool/FrontendTool.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,17 @@ static bool precompileBridgingHeader(CompilerInvocation &Invocation,
559559
.InputsAndOutputs.getSingleOutputFilename());
560560
}
561561

562+
static bool buildModuleFromParseableInterface(CompilerInvocation &Invocation,
563+
CompilerInstance &Instance) {
564+
const auto &InputsAndOutputs =
565+
Invocation.getFrontendOptions().InputsAndOutputs;
566+
assert(InputsAndOutputs.hasSingleInput());
567+
StringRef InputPath = InputsAndOutputs.getFilenameOfFirstInput();
568+
return ParseableInterfaceModuleLoader::buildSwiftModuleFromSwiftInterface(
569+
Instance.getASTContext(), Invocation.getClangModuleCachePath(),
570+
Invocation.getModuleName(), InputPath, Invocation.getOutputFilename());
571+
}
572+
562573
static bool compileLLVMIR(CompilerInvocation &Invocation,
563574
CompilerInstance &Instance,
564575
UnifiedStatsReporter *Stats) {
@@ -923,6 +934,9 @@ static bool performCompile(CompilerInstance &Instance,
923934
if (Action == FrontendOptions::ActionType::EmitPCH)
924935
return precompileBridgingHeader(Invocation, Instance);
925936

937+
if (Action == FrontendOptions::ActionType::BuildModuleFromParseableInterface)
938+
return buildModuleFromParseableInterface(Invocation, Instance);
939+
926940
if (Invocation.getInputKind() == InputFileKind::LLVM)
927941
return compileLLVMIR(Invocation, Instance, Stats);
928942

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// swift-interface-format-version: 1.0
2+
// swift-module-flags: -parse-stdlib
3+
4+
// Note that the "-parse-stdlib" is picked up from the module flags. It should
5+
// not be written in any of the invocations below.
6+
7+
// RUN: %empty-directory(%t)
8+
// RUN: %target-swift-frontend -build-module-from-parseable-interface -o %t/ParseStdlib.swiftmodule %s
9+
// RUN: %target-swift-ide-test -print-module -module-to-print ParseStdlib -I %t -source-filename x -print-interface | %FileCheck %s
10+
11+
// CHECK: func test(_: Int42)
12+
public func test(_: Builtin.Int42) {}

0 commit comments

Comments
 (0)