Skip to content

Commit ce5b1a1

Browse files
authored
Merge pull request #84392 from rjmansfield/sil-ir-extra-outputs
Add frontend options to write SIL and LLVM IR as additional compilation output
2 parents 1777e31 + ba0ce8a commit ce5b1a1

File tree

15 files changed

+372
-83
lines changed

15 files changed

+372
-83
lines changed

include/swift/AST/IRGenRequests.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ struct IRGenDescriptor {
151151
const PrimarySpecificPaths &PSPs;
152152
StringRef PrivateDiscriminator;
153153
ArrayRef<std::string> parallelOutputFilenames;
154+
ArrayRef<std::string> parallelIROutputFilenames;
154155
llvm::GlobalVariable **outModuleHash;
155156
llvm::raw_pwrite_stream *out = nullptr;
156157

@@ -188,6 +189,7 @@ struct IRGenDescriptor {
188189
PSPs,
189190
PrivateDiscriminator,
190191
{},
192+
{},
191193
outModuleHash};
192194
}
193195

@@ -197,6 +199,7 @@ struct IRGenDescriptor {
197199
std::unique_ptr<SILModule> &&SILMod, StringRef ModuleName,
198200
const PrimarySpecificPaths &PSPs, SymsToEmit symsToEmit = std::nullopt,
199201
ArrayRef<std::string> parallelOutputFilenames = {},
202+
ArrayRef<std::string> parallelIROutputFilenames = {},
200203
llvm::GlobalVariable **outModuleHash = nullptr) {
201204
return IRGenDescriptor{M,
202205
symsToEmit,
@@ -209,6 +212,7 @@ struct IRGenDescriptor {
209212
PSPs,
210213
"",
211214
parallelOutputFilenames,
215+
parallelIROutputFilenames,
212216
outModuleHash};
213217
}
214218

include/swift/Basic/SupplementaryOutputPaths.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,9 @@ OUTPUT(YAMLOptRecordPath, TY_YAMLOptRecord)
166166

167167
/// The output path for bitstream optimization record file.
168168
OUTPUT(BitstreamOptRecordPath, TY_BitstreamOptRecord)
169+
170+
/// The output path to which we should output SIL as extra compilation output.
171+
OUTPUT(SILOutputPath, TY_SIL)
172+
173+
/// The output path to which we should output LLVM IR as extra compilation output.
174+
OUTPUT(LLVMIROutputPath, TY_LLVM_IR)

include/swift/FrontendTool/FrontendTool.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ int performFrontend(ArrayRef<const char *> args,
7979
FrontendObserver *observer = nullptr);
8080

8181
bool performCompileStepsPostSema(CompilerInstance &Instance, int &ReturnValue,
82-
FrontendObserver *observer);
82+
FrontendObserver *observer,
83+
ArrayRef<const char *> CommandLineArgs);
8384

8485
} // namespace swift
8586

include/swift/Option/FrontendOptions.td

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@ def emit_module_semantic_info_path
6363
: Separate<["-"], "emit-module-semantic-info-path">, MetaVarName<"<path>">,
6464
HelpText<"Output semantic info of current module to <path>">;
6565

66+
def sil_output_path
67+
: Separate<["-"], "sil-output-path">, MetaVarName<"<path>">,
68+
Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath,
69+
SupplementaryOutput, CacheInvariant]>,
70+
HelpText<"Output SIL to <path> as additional output during compilation">;
71+
72+
def ir_output_path
73+
: Separate<["-"], "ir-output-path">, MetaVarName<"<path>">,
74+
Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath,
75+
SupplementaryOutput, CacheInvariant]>,
76+
HelpText<"Output LLVM IR to <path> as additional output during compilation">;
77+
6678
def diagnostic_documentation_path
6779
: Separate<["-"], "diagnostic-documentation-path">, MetaVarName<"<path>">,
6880
HelpText<"Path to diagnostic documentation resources">;
@@ -268,7 +280,7 @@ def serialize_dependency_scan_cache : Flag<["-"], "serialize-dependency-scan-cac
268280

269281
def reuse_dependency_scan_cache : Flag<["-"], "load-dependency-scan-cache">,
270282
HelpText<"For performing a dependency scan, deserialize the scanner's internal state from a prior scan.">;
271-
283+
272284
def validate_prior_dependency_scan_cache : Flag<["-"], "validate-prior-dependency-scan-cache">,
273285
HelpText<"For performing a dependency scan with a prior scanner state, validate module dependencies.">;
274286

include/swift/Subsystems.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,10 @@ namespace swift {
250250
GeneratedModule
251251
performIRGeneration(ModuleDecl *M, const IRGenOptions &Opts,
252252
const TBDGenOptions &TBDOpts,
253-
std::unique_ptr<SILModule> SILMod,
254-
StringRef ModuleName, const PrimarySpecificPaths &PSPs,
253+
std::unique_ptr<SILModule> SILMod, StringRef ModuleName,
254+
const PrimarySpecificPaths &PSPs,
255255
ArrayRef<std::string> parallelOutputFilenames,
256+
ArrayRef<std::string> parallelIROutputFilenames,
256257
llvm::GlobalVariable **outModuleHash = nullptr);
257258

258259
/// Turn the given Swift file into LLVM IR and return the generated module.

lib/Driver/ToolChains.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,12 @@ void ToolChain::JobContext::addFrontendSupplementaryOutputArguments(
968968
addOutputsOfType(arguments, Output, Args,
969969
file_types::TY_SwiftModuleSummaryFile,
970970
"-emit-module-summary-path");
971+
972+
// Add extra output paths for SIL and LLVM IR
973+
addOutputsOfType(arguments, Output, Args, file_types::TY_SIL,
974+
"-sil-output-path");
975+
addOutputsOfType(arguments, Output, Args, file_types::TY_LLVM_IR,
976+
"-ir-output-path");
971977
}
972978

973979
ToolChain::InvocationInfo
@@ -1252,6 +1258,12 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job,
12521258
addOutputsOfType(Arguments, context.Output, context.Args, file_types::TY_TBD,
12531259
"-emit-tbd-path");
12541260

1261+
// Add extra output paths for SIL and LLVM IR
1262+
addOutputsOfType(Arguments, context.Output, context.Args, file_types::TY_SIL,
1263+
"-sil-output-path");
1264+
addOutputsOfType(Arguments, context.Output, context.Args,
1265+
file_types::TY_LLVM_IR, "-ir-output-path");
1266+
12551267
context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph);
12561268
context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph_dir);
12571269
context.Args.AddLastArg(Arguments, options::OPT_include_spi_symbols);

lib/Frontend/ArgsToFrontendOutputsConverter.cpp

Lines changed: 95 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -275,15 +275,15 @@ SupplementaryOutputPathsComputer::computeOutputPaths() const {
275275

276276
if (InputsAndOutputs.hasPrimaryInputs())
277277
assert(OutputFiles.size() == pathsFromUser->size());
278-
else if (InputsAndOutputs.isSingleThreadedWMO())
279-
assert(OutputFiles.size() == pathsFromUser->size() &&
280-
pathsFromUser->size() == 1);
281278
else {
282-
// Multi-threaded WMO is the exception
283-
assert(OutputFiles.size() == InputsAndOutputs.inputCount() &&
284-
pathsFromUser->size() == (InputsAndOutputs.hasInputs() ? 1 : 0));
279+
if (!InputsAndOutputs.isSingleThreadedWMO()) {
280+
assert(OutputFiles.size() == InputsAndOutputs.inputCount());
281+
}
282+
assert(pathsFromUser->size() == 1 ||
283+
pathsFromUser->size() == InputsAndOutputs.inputCount());
285284
}
286285

286+
// For other cases, process the paths normally
287287
std::vector<SupplementaryOutputPaths> outputPaths;
288288
unsigned i = 0;
289289
bool hadError = InputsAndOutputs.forEachInputProducingSupplementaryOutput(
@@ -380,39 +380,78 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments()
380380
options::OPT_emit_module_semantic_info_path);
381381
auto optRecordOutput = getSupplementaryFilenamesFromArguments(
382382
options::OPT_save_optimization_record_path);
383+
auto silOutput =
384+
getSupplementaryFilenamesFromArguments(options::OPT_sil_output_path);
385+
auto irOutput =
386+
getSupplementaryFilenamesFromArguments(options::OPT_ir_output_path);
383387
if (!clangHeaderOutput || !moduleOutput || !moduleDocOutput ||
384388
!dependenciesFile || !referenceDependenciesFile ||
385389
!serializedDiagnostics || !loadedModuleTrace || !TBD ||
386-
!moduleInterfaceOutput || !privateModuleInterfaceOutput || !packageModuleInterfaceOutput ||
387-
!moduleSourceInfoOutput || !moduleSummaryOutput || !abiDescriptorOutput ||
388-
!moduleSemanticInfoOutput || !optRecordOutput) {
390+
!moduleInterfaceOutput || !privateModuleInterfaceOutput ||
391+
!packageModuleInterfaceOutput || !moduleSourceInfoOutput ||
392+
!moduleSummaryOutput || !abiDescriptorOutput ||
393+
!moduleSemanticInfoOutput || !optRecordOutput || !silOutput ||
394+
!irOutput) {
389395
return std::nullopt;
390396
}
391397
std::vector<SupplementaryOutputPaths> result;
392398

393-
const unsigned N =
394-
InputsAndOutputs.countOfFilesProducingSupplementaryOutput();
399+
// In WMO mode with multiple IR output paths, we need to create one
400+
// SupplementaryOutputPaths per input file, not just one for the module
401+
unsigned N = InputsAndOutputs.countOfFilesProducingSupplementaryOutput();
402+
if (!InputsAndOutputs.hasPrimaryInputs() && irOutput->size() > 1) {
403+
// WMO mode with multiple IR outputs: use input count instead of 1
404+
N = InputsAndOutputs.inputCount();
405+
}
406+
407+
// Find the index of SIL output path matching module name
408+
auto findSILIndexForModuleName = [&]() -> unsigned {
409+
if (!InputsAndOutputs.hasPrimaryInputs() && silOutput->size() > 1) {
410+
// In WMO mode with multiple SIL output paths, find the one whose matches
411+
// module name
412+
for (unsigned i = 0; i < silOutput->size(); ++i) {
413+
StringRef silPath = (*silOutput)[i];
414+
if (!silPath.empty()) {
415+
StringRef basename = llvm::sys::path::stem(silPath);
416+
if (basename == ModuleName) {
417+
return i;
418+
}
419+
}
420+
}
421+
// If no match found, fall back to first
422+
return 0;
423+
}
424+
return 0;
425+
};
426+
427+
unsigned silOutputIndex = findSILIndexForModuleName();
428+
395429
for (unsigned i = 0; i < N; ++i) {
396430
SupplementaryOutputPaths sop;
397-
sop.ClangHeaderOutputPath = (*clangHeaderOutput)[i];
398-
sop.ModuleOutputPath = (*moduleOutput)[i];
399-
sop.ModuleDocOutputPath = (*moduleDocOutput)[i];
400-
sop.DependenciesFilePath = (*dependenciesFile)[i];
401-
sop.ReferenceDependenciesFilePath = (*referenceDependenciesFile)[i];
402-
sop.SerializedDiagnosticsPath = (*serializedDiagnostics)[i];
403-
sop.LoadedModuleTracePath = (*loadedModuleTrace)[i];
404-
sop.TBDPath = (*TBD)[i];
405-
sop.ModuleInterfaceOutputPath = (*moduleInterfaceOutput)[i];
406-
sop.PrivateModuleInterfaceOutputPath = (*privateModuleInterfaceOutput)[i];
407-
sop.PackageModuleInterfaceOutputPath = (*packageModuleInterfaceOutput)[i];
408-
sop.ModuleSourceInfoOutputPath = (*moduleSourceInfoOutput)[i];
409-
sop.ModuleSummaryOutputPath = (*moduleSummaryOutput)[i];
410-
sop.ABIDescriptorOutputPath = (*abiDescriptorOutput)[i];
411-
sop.APIDescriptorOutputPath = (*apiDescriptorOutput)[i];
412-
sop.ConstValuesOutputPath = (*constValuesOutput)[i];
413-
sop.ModuleSemanticInfoOutputPath = (*moduleSemanticInfoOutput)[i];
414-
sop.YAMLOptRecordPath = (*optRecordOutput)[i];
415-
sop.BitstreamOptRecordPath = (*optRecordOutput)[i];
431+
// In multi-threaded WMO with multiple IR outputs, most supplementary outputs
432+
// are per-module (size 1), only IR is per-file. Use index 0 for module outputs.
433+
unsigned moduleIndex = (!InputsAndOutputs.hasPrimaryInputs() && irOutput->size() > 1) ? 0 : i;
434+
sop.ClangHeaderOutputPath = (*clangHeaderOutput)[moduleIndex];
435+
sop.ModuleOutputPath = (*moduleOutput)[moduleIndex];
436+
sop.ModuleDocOutputPath = (*moduleDocOutput)[moduleIndex];
437+
sop.DependenciesFilePath = (*dependenciesFile)[moduleIndex];
438+
sop.ReferenceDependenciesFilePath = (*referenceDependenciesFile)[moduleIndex];
439+
sop.SerializedDiagnosticsPath = (*serializedDiagnostics)[moduleIndex];
440+
sop.LoadedModuleTracePath = (*loadedModuleTrace)[moduleIndex];
441+
sop.TBDPath = (*TBD)[moduleIndex];
442+
sop.ModuleInterfaceOutputPath = (*moduleInterfaceOutput)[moduleIndex];
443+
sop.PrivateModuleInterfaceOutputPath = (*privateModuleInterfaceOutput)[moduleIndex];
444+
sop.PackageModuleInterfaceOutputPath = (*packageModuleInterfaceOutput)[moduleIndex];
445+
sop.ModuleSourceInfoOutputPath = (*moduleSourceInfoOutput)[moduleIndex];
446+
sop.ModuleSummaryOutputPath = (*moduleSummaryOutput)[moduleIndex];
447+
sop.ABIDescriptorOutputPath = (*abiDescriptorOutput)[moduleIndex];
448+
sop.APIDescriptorOutputPath = (*apiDescriptorOutput)[moduleIndex];
449+
sop.ConstValuesOutputPath = (*constValuesOutput)[moduleIndex];
450+
sop.ModuleSemanticInfoOutputPath = (*moduleSemanticInfoOutput)[moduleIndex];
451+
sop.YAMLOptRecordPath = (*optRecordOutput)[moduleIndex];
452+
sop.BitstreamOptRecordPath = (*optRecordOutput)[moduleIndex];
453+
sop.SILOutputPath = (*silOutput)[silOutputIndex];
454+
sop.LLVMIROutputPath = (*irOutput)[i];
416455
result.push_back(sop);
417456
}
418457
return result;
@@ -439,6 +478,15 @@ SupplementaryOutputPathsComputer::getSupplementaryFilenamesFromArguments(
439478
paths.emplace_back();
440479
return paths;
441480
}
481+
// Special handling for SIL and IR output paths: allow multiple paths per file
482+
// type
483+
else if ((pathID == options::OPT_sil_output_path ||
484+
pathID == options::OPT_ir_output_path) &&
485+
paths.size() > N) {
486+
// For parallel compilation, we can have multiple SIL/IR output paths
487+
// so return all the paths.
488+
return paths;
489+
}
442490

443491
if (paths.empty())
444492
return std::vector<std::string>(N, std::string());
@@ -613,6 +661,9 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput(
613661
file_types::TY_BitstreamOptRecord, "",
614662
defaultSupplementaryOutputPathExcludingExtension);
615663

664+
auto SILOutputPath = pathsFromArguments.SILOutputPath;
665+
auto LLVMIROutputPath = pathsFromArguments.LLVMIROutputPath;
666+
616667
SupplementaryOutputPaths sop;
617668
sop.ClangHeaderOutputPath = clangHeaderOutputPath;
618669
sop.ModuleOutputPath = moduleOutputPath;
@@ -635,6 +686,8 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput(
635686
sop.ModuleSemanticInfoOutputPath = ModuleSemanticInfoOutputPath;
636687
sop.YAMLOptRecordPath = YAMLOptRecordPath;
637688
sop.BitstreamOptRecordPath = bitstreamOptRecordPath;
689+
sop.SILOutputPath = SILOutputPath;
690+
sop.LLVMIROutputPath = LLVMIROutputPath;
638691
return sop;
639692
}
640693

@@ -741,18 +794,18 @@ createFromTypeToPathMap(const TypeToPathMap *map) {
741794

742795
std::optional<std::vector<SupplementaryOutputPaths>>
743796
SupplementaryOutputPathsComputer::readSupplementaryOutputFileMap() const {
744-
if (Arg *A = Args.getLastArg(options::OPT_emit_objc_header_path,
745-
options::OPT_emit_module_path,
746-
options::OPT_emit_module_doc_path,
747-
options::OPT_emit_dependencies_path,
748-
options::OPT_emit_reference_dependencies_path,
749-
options::OPT_serialize_diagnostics_path,
750-
options::OPT_emit_loaded_module_trace_path,
751-
options::OPT_emit_module_interface_path,
752-
options::OPT_emit_private_module_interface_path,
753-
options::OPT_emit_package_module_interface_path,
754-
options::OPT_emit_module_source_info_path,
755-
options::OPT_emit_tbd_path)) {
797+
if (Arg *A = Args.getLastArg(
798+
options::OPT_emit_objc_header_path, options::OPT_emit_module_path,
799+
options::OPT_emit_module_doc_path,
800+
options::OPT_emit_dependencies_path,
801+
options::OPT_emit_reference_dependencies_path,
802+
options::OPT_serialize_diagnostics_path,
803+
options::OPT_emit_loaded_module_trace_path,
804+
options::OPT_emit_module_interface_path,
805+
options::OPT_emit_private_module_interface_path,
806+
options::OPT_emit_package_module_interface_path,
807+
options::OPT_emit_module_source_info_path, options::OPT_emit_tbd_path,
808+
options::OPT_sil_output_path, options::OPT_ir_output_path)) {
756809
Diags.diagnose(SourceLoc(),
757810
diag::error_cannot_have_supplementary_outputs,
758811
A->getSpelling(), "-supplementary-output-file-map");

0 commit comments

Comments
 (0)