-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Add frontend options to write SIL and LLVM IR as additional compilation output #84392
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -275,15 +275,15 @@ SupplementaryOutputPathsComputer::computeOutputPaths() const { | |
|
||
if (InputsAndOutputs.hasPrimaryInputs()) | ||
assert(OutputFiles.size() == pathsFromUser->size()); | ||
else if (InputsAndOutputs.isSingleThreadedWMO()) | ||
assert(OutputFiles.size() == pathsFromUser->size() && | ||
pathsFromUser->size() == 1); | ||
else { | ||
// Multi-threaded WMO is the exception | ||
assert(OutputFiles.size() == InputsAndOutputs.inputCount() && | ||
pathsFromUser->size() == (InputsAndOutputs.hasInputs() ? 1 : 0)); | ||
if (!InputsAndOutputs.isSingleThreadedWMO()) { | ||
assert(OutputFiles.size() == InputsAndOutputs.inputCount()); | ||
} | ||
assert(pathsFromUser->size() == 1 || | ||
pathsFromUser->size() == InputsAndOutputs.inputCount()); | ||
} | ||
|
||
// For other cases, process the paths normally | ||
std::vector<SupplementaryOutputPaths> outputPaths; | ||
unsigned i = 0; | ||
bool hadError = InputsAndOutputs.forEachInputProducingSupplementaryOutput( | ||
|
@@ -380,39 +380,78 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() | |
options::OPT_emit_module_semantic_info_path); | ||
auto optRecordOutput = getSupplementaryFilenamesFromArguments( | ||
options::OPT_save_optimization_record_path); | ||
auto silOutput = | ||
getSupplementaryFilenamesFromArguments(options::OPT_sil_output_path); | ||
auto irOutput = | ||
getSupplementaryFilenamesFromArguments(options::OPT_ir_output_path); | ||
if (!clangHeaderOutput || !moduleOutput || !moduleDocOutput || | ||
!dependenciesFile || !referenceDependenciesFile || | ||
!serializedDiagnostics || !loadedModuleTrace || !TBD || | ||
!moduleInterfaceOutput || !privateModuleInterfaceOutput || !packageModuleInterfaceOutput || | ||
!moduleSourceInfoOutput || !moduleSummaryOutput || !abiDescriptorOutput || | ||
!moduleSemanticInfoOutput || !optRecordOutput) { | ||
!moduleInterfaceOutput || !privateModuleInterfaceOutput || | ||
!packageModuleInterfaceOutput || !moduleSourceInfoOutput || | ||
!moduleSummaryOutput || !abiDescriptorOutput || | ||
!moduleSemanticInfoOutput || !optRecordOutput || !silOutput || | ||
!irOutput) { | ||
return std::nullopt; | ||
} | ||
std::vector<SupplementaryOutputPaths> result; | ||
|
||
const unsigned N = | ||
InputsAndOutputs.countOfFilesProducingSupplementaryOutput(); | ||
// In WMO mode with multiple IR output paths, we need to create one | ||
// SupplementaryOutputPaths per input file, not just one for the module | ||
unsigned N = InputsAndOutputs.countOfFilesProducingSupplementaryOutput(); | ||
if (!InputsAndOutputs.hasPrimaryInputs() && irOutput->size() > 1) { | ||
// WMO mode with multiple IR outputs: use input count instead of 1 | ||
N = InputsAndOutputs.inputCount(); | ||
} | ||
|
||
// Find the index of SIL output path matching module name | ||
auto findSILIndexForModuleName = [&]() -> unsigned { | ||
if (!InputsAndOutputs.hasPrimaryInputs() && silOutput->size() > 1) { | ||
// In WMO mode with multiple SIL output paths, find the one whose matches | ||
// module name | ||
for (unsigned i = 0; i < silOutput->size(); ++i) { | ||
StringRef silPath = (*silOutput)[i]; | ||
if (!silPath.empty()) { | ||
StringRef basename = llvm::sys::path::stem(silPath); | ||
if (basename == ModuleName) { | ||
return i; | ||
} | ||
} | ||
} | ||
// If no match found, fall back to first | ||
return 0; | ||
} | ||
return 0; | ||
}; | ||
|
||
unsigned silOutputIndex = findSILIndexForModuleName(); | ||
|
||
for (unsigned i = 0; i < N; ++i) { | ||
SupplementaryOutputPaths sop; | ||
sop.ClangHeaderOutputPath = (*clangHeaderOutput)[i]; | ||
sop.ModuleOutputPath = (*moduleOutput)[i]; | ||
sop.ModuleDocOutputPath = (*moduleDocOutput)[i]; | ||
sop.DependenciesFilePath = (*dependenciesFile)[i]; | ||
sop.ReferenceDependenciesFilePath = (*referenceDependenciesFile)[i]; | ||
sop.SerializedDiagnosticsPath = (*serializedDiagnostics)[i]; | ||
sop.LoadedModuleTracePath = (*loadedModuleTrace)[i]; | ||
sop.TBDPath = (*TBD)[i]; | ||
sop.ModuleInterfaceOutputPath = (*moduleInterfaceOutput)[i]; | ||
sop.PrivateModuleInterfaceOutputPath = (*privateModuleInterfaceOutput)[i]; | ||
sop.PackageModuleInterfaceOutputPath = (*packageModuleInterfaceOutput)[i]; | ||
sop.ModuleSourceInfoOutputPath = (*moduleSourceInfoOutput)[i]; | ||
sop.ModuleSummaryOutputPath = (*moduleSummaryOutput)[i]; | ||
sop.ABIDescriptorOutputPath = (*abiDescriptorOutput)[i]; | ||
sop.APIDescriptorOutputPath = (*apiDescriptorOutput)[i]; | ||
sop.ConstValuesOutputPath = (*constValuesOutput)[i]; | ||
sop.ModuleSemanticInfoOutputPath = (*moduleSemanticInfoOutput)[i]; | ||
sop.YAMLOptRecordPath = (*optRecordOutput)[i]; | ||
sop.BitstreamOptRecordPath = (*optRecordOutput)[i]; | ||
// In multi-threaded WMO with multiple IR outputs, most supplementary outputs | ||
// are per-module (size 1), only IR is per-file. Use index 0 for module outputs. | ||
unsigned moduleIndex = (!InputsAndOutputs.hasPrimaryInputs() && irOutput->size() > 1) ? 0 : i; | ||
sop.ClangHeaderOutputPath = (*clangHeaderOutput)[moduleIndex]; | ||
sop.ModuleOutputPath = (*moduleOutput)[moduleIndex]; | ||
sop.ModuleDocOutputPath = (*moduleDocOutput)[moduleIndex]; | ||
sop.DependenciesFilePath = (*dependenciesFile)[moduleIndex]; | ||
sop.ReferenceDependenciesFilePath = (*referenceDependenciesFile)[moduleIndex]; | ||
sop.SerializedDiagnosticsPath = (*serializedDiagnostics)[moduleIndex]; | ||
sop.LoadedModuleTracePath = (*loadedModuleTrace)[moduleIndex]; | ||
sop.TBDPath = (*TBD)[moduleIndex]; | ||
sop.ModuleInterfaceOutputPath = (*moduleInterfaceOutput)[moduleIndex]; | ||
sop.PrivateModuleInterfaceOutputPath = (*privateModuleInterfaceOutput)[moduleIndex]; | ||
sop.PackageModuleInterfaceOutputPath = (*packageModuleInterfaceOutput)[moduleIndex]; | ||
sop.ModuleSourceInfoOutputPath = (*moduleSourceInfoOutput)[moduleIndex]; | ||
sop.ModuleSummaryOutputPath = (*moduleSummaryOutput)[moduleIndex]; | ||
sop.ABIDescriptorOutputPath = (*abiDescriptorOutput)[moduleIndex]; | ||
sop.APIDescriptorOutputPath = (*apiDescriptorOutput)[moduleIndex]; | ||
sop.ConstValuesOutputPath = (*constValuesOutput)[moduleIndex]; | ||
sop.ModuleSemanticInfoOutputPath = (*moduleSemanticInfoOutput)[moduleIndex]; | ||
sop.YAMLOptRecordPath = (*optRecordOutput)[moduleIndex]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, not sure this is right for optimization record ( Can you check that there will be two .yaml or two .bitstream files produced after this patch for the following test case:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it generates two separate files.
Interestingly the swiftc 6.2 I have installed generates only main.opt.bitstream or main.opt.yaml There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
👍
Yes, I fixed this behavior recently. |
||
sop.BitstreamOptRecordPath = (*optRecordOutput)[moduleIndex]; | ||
sop.SILOutputPath = (*silOutput)[silOutputIndex]; | ||
sop.LLVMIROutputPath = (*irOutput)[i]; | ||
result.push_back(sop); | ||
} | ||
return result; | ||
|
@@ -439,6 +478,15 @@ SupplementaryOutputPathsComputer::getSupplementaryFilenamesFromArguments( | |
paths.emplace_back(); | ||
return paths; | ||
} | ||
// Special handling for SIL and IR output paths: allow multiple paths per file | ||
// type | ||
else if ((pathID == options::OPT_sil_output_path || | ||
pathID == options::OPT_ir_output_path) && | ||
paths.size() > N) { | ||
// For parallel compilation, we can have multiple SIL/IR output paths | ||
// so return all the paths. | ||
return paths; | ||
} | ||
|
||
if (paths.empty()) | ||
return std::vector<std::string>(N, std::string()); | ||
|
@@ -613,6 +661,9 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( | |
file_types::TY_BitstreamOptRecord, "", | ||
defaultSupplementaryOutputPathExcludingExtension); | ||
|
||
auto SILOutputPath = pathsFromArguments.SILOutputPath; | ||
auto LLVMIROutputPath = pathsFromArguments.LLVMIROutputPath; | ||
|
||
SupplementaryOutputPaths sop; | ||
sop.ClangHeaderOutputPath = clangHeaderOutputPath; | ||
sop.ModuleOutputPath = moduleOutputPath; | ||
|
@@ -635,6 +686,8 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( | |
sop.ModuleSemanticInfoOutputPath = ModuleSemanticInfoOutputPath; | ||
sop.YAMLOptRecordPath = YAMLOptRecordPath; | ||
sop.BitstreamOptRecordPath = bitstreamOptRecordPath; | ||
sop.SILOutputPath = SILOutputPath; | ||
sop.LLVMIROutputPath = LLVMIROutputPath; | ||
return sop; | ||
} | ||
|
||
|
@@ -741,18 +794,18 @@ createFromTypeToPathMap(const TypeToPathMap *map) { | |
|
||
std::optional<std::vector<SupplementaryOutputPaths>> | ||
SupplementaryOutputPathsComputer::readSupplementaryOutputFileMap() const { | ||
if (Arg *A = Args.getLastArg(options::OPT_emit_objc_header_path, | ||
options::OPT_emit_module_path, | ||
options::OPT_emit_module_doc_path, | ||
options::OPT_emit_dependencies_path, | ||
options::OPT_emit_reference_dependencies_path, | ||
options::OPT_serialize_diagnostics_path, | ||
options::OPT_emit_loaded_module_trace_path, | ||
options::OPT_emit_module_interface_path, | ||
options::OPT_emit_private_module_interface_path, | ||
options::OPT_emit_package_module_interface_path, | ||
options::OPT_emit_module_source_info_path, | ||
options::OPT_emit_tbd_path)) { | ||
if (Arg *A = Args.getLastArg( | ||
options::OPT_emit_objc_header_path, options::OPT_emit_module_path, | ||
options::OPT_emit_module_doc_path, | ||
options::OPT_emit_dependencies_path, | ||
options::OPT_emit_reference_dependencies_path, | ||
options::OPT_serialize_diagnostics_path, | ||
options::OPT_emit_loaded_module_trace_path, | ||
options::OPT_emit_module_interface_path, | ||
options::OPT_emit_private_module_interface_path, | ||
options::OPT_emit_package_module_interface_path, | ||
options::OPT_emit_module_source_info_path, options::OPT_emit_tbd_path, | ||
options::OPT_sil_output_path, options::OPT_ir_output_path)) { | ||
Diags.diagnose(SourceLoc(), | ||
diag::error_cannot_have_supplementary_outputs, | ||
A->getSpelling(), "-supplementary-output-file-map"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these are being modeled as additional supplementary outputs they also need a flag
SupplementaryOutput
:swift/include/swift/Option/Options.td
Line 58 in 391a18d