mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Add frontend options to write SIL and LLVM IR as additional compilation output.
This commit adds -sil-output-path and -ir-output-path frontend options that allow generating SIL and LLVM IR files as supplementary outputs during normal compilation. These options can be useful for debugging and analysis tools workflows that need access to intermediate compilation artifacts without requiring separate compiler invocations. Expected behaviour: Primary File mode: - SIL: Generates one .sil file per source file - IR: Generates one .ll file per source file Single-threaded WMO mode: - SIL: Generates one .sil file for the entire module - IR: Generates one .ll file for the entire module Multi-threaded WMO mode: - SIL: Generates one .sil file for the entire module - IR: Generates separate .ll files per source file File Maps with WMO: - Both SIL and IR outputs using first entry's naming, which is consistent with the behaviour of other supplementary outputs. rdar://160297898
This commit is contained in:
@@ -151,6 +151,7 @@ struct IRGenDescriptor {
|
||||
const PrimarySpecificPaths &PSPs;
|
||||
StringRef PrivateDiscriminator;
|
||||
ArrayRef<std::string> parallelOutputFilenames;
|
||||
ArrayRef<std::string> parallelIROutputFilenames;
|
||||
llvm::GlobalVariable **outModuleHash;
|
||||
llvm::raw_pwrite_stream *out = nullptr;
|
||||
|
||||
@@ -188,6 +189,7 @@ public:
|
||||
PSPs,
|
||||
PrivateDiscriminator,
|
||||
{},
|
||||
{},
|
||||
outModuleHash};
|
||||
}
|
||||
|
||||
@@ -197,6 +199,7 @@ public:
|
||||
std::unique_ptr<SILModule> &&SILMod, StringRef ModuleName,
|
||||
const PrimarySpecificPaths &PSPs, SymsToEmit symsToEmit = std::nullopt,
|
||||
ArrayRef<std::string> parallelOutputFilenames = {},
|
||||
ArrayRef<std::string> parallelIROutputFilenames = {},
|
||||
llvm::GlobalVariable **outModuleHash = nullptr) {
|
||||
return IRGenDescriptor{M,
|
||||
symsToEmit,
|
||||
@@ -209,6 +212,7 @@ public:
|
||||
PSPs,
|
||||
"",
|
||||
parallelOutputFilenames,
|
||||
parallelIROutputFilenames,
|
||||
outModuleHash};
|
||||
}
|
||||
|
||||
|
||||
@@ -166,3 +166,9 @@ OUTPUT(YAMLOptRecordPath, TY_YAMLOptRecord)
|
||||
|
||||
/// The output path for bitstream optimization record file.
|
||||
OUTPUT(BitstreamOptRecordPath, TY_BitstreamOptRecord)
|
||||
|
||||
/// The output path to which we should output SIL as extra compilation output.
|
||||
OUTPUT(SILOutputPath, TY_SIL)
|
||||
|
||||
/// The output path to which we should output LLVM IR as extra compilation output.
|
||||
OUTPUT(LLVMIROutputPath, TY_LLVM_IR)
|
||||
|
||||
@@ -79,7 +79,8 @@ int performFrontend(ArrayRef<const char *> args,
|
||||
FrontendObserver *observer = nullptr);
|
||||
|
||||
bool performCompileStepsPostSema(CompilerInstance &Instance, int &ReturnValue,
|
||||
FrontendObserver *observer);
|
||||
FrontendObserver *observer,
|
||||
ArrayRef<const char *> CommandLineArgs);
|
||||
|
||||
} // namespace swift
|
||||
|
||||
|
||||
@@ -63,6 +63,18 @@ def emit_module_semantic_info_path
|
||||
: Separate<["-"], "emit-module-semantic-info-path">, MetaVarName<"<path>">,
|
||||
HelpText<"Output semantic info of current module to <path>">;
|
||||
|
||||
def sil_output_path
|
||||
: Separate<["-"], "sil-output-path">, MetaVarName<"<path>">,
|
||||
Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath,
|
||||
SupplementaryOutput, CacheInvariant]>,
|
||||
HelpText<"Output SIL to <path> as additional output during compilation">;
|
||||
|
||||
def ir_output_path
|
||||
: Separate<["-"], "ir-output-path">, MetaVarName<"<path>">,
|
||||
Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath,
|
||||
SupplementaryOutput, CacheInvariant]>,
|
||||
HelpText<"Output LLVM IR to <path> as additional output during compilation">;
|
||||
|
||||
def diagnostic_documentation_path
|
||||
: Separate<["-"], "diagnostic-documentation-path">, MetaVarName<"<path>">,
|
||||
HelpText<"Path to diagnostic documentation resources">;
|
||||
@@ -266,7 +278,7 @@ def serialize_dependency_scan_cache : Flag<["-"], "serialize-dependency-scan-cac
|
||||
|
||||
def reuse_dependency_scan_cache : Flag<["-"], "load-dependency-scan-cache">,
|
||||
HelpText<"For performing a dependency scan, deserialize the scanner's internal state from a prior scan.">;
|
||||
|
||||
|
||||
def validate_prior_dependency_scan_cache : Flag<["-"], "validate-prior-dependency-scan-cache">,
|
||||
HelpText<"For performing a dependency scan with a prior scanner state, validate module dependencies.">;
|
||||
|
||||
|
||||
@@ -250,9 +250,10 @@ namespace swift {
|
||||
GeneratedModule
|
||||
performIRGeneration(ModuleDecl *M, const IRGenOptions &Opts,
|
||||
const TBDGenOptions &TBDOpts,
|
||||
std::unique_ptr<SILModule> SILMod,
|
||||
StringRef ModuleName, const PrimarySpecificPaths &PSPs,
|
||||
std::unique_ptr<SILModule> SILMod, StringRef ModuleName,
|
||||
const PrimarySpecificPaths &PSPs,
|
||||
ArrayRef<std::string> parallelOutputFilenames,
|
||||
ArrayRef<std::string> parallelIROutputFilenames,
|
||||
llvm::GlobalVariable **outModuleHash = nullptr);
|
||||
|
||||
/// Turn the given Swift file into LLVM IR and return the generated module.
|
||||
|
||||
@@ -953,6 +953,12 @@ void ToolChain::JobContext::addFrontendSupplementaryOutputArguments(
|
||||
addOutputsOfType(arguments, Output, Args,
|
||||
file_types::TY_SwiftModuleSummaryFile,
|
||||
"-emit-module-summary-path");
|
||||
|
||||
// Add extra output paths for SIL and LLVM IR
|
||||
addOutputsOfType(arguments, Output, Args, file_types::TY_SIL,
|
||||
"-sil-output-path");
|
||||
addOutputsOfType(arguments, Output, Args, file_types::TY_LLVM_IR,
|
||||
"-ir-output-path");
|
||||
}
|
||||
|
||||
ToolChain::InvocationInfo
|
||||
@@ -1237,6 +1243,12 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job,
|
||||
addOutputsOfType(Arguments, context.Output, context.Args, file_types::TY_TBD,
|
||||
"-emit-tbd-path");
|
||||
|
||||
// Add extra output paths for SIL and LLVM IR
|
||||
addOutputsOfType(Arguments, context.Output, context.Args, file_types::TY_SIL,
|
||||
"-sil-output-path");
|
||||
addOutputsOfType(Arguments, context.Output, context.Args,
|
||||
file_types::TY_LLVM_IR, "-ir-output-path");
|
||||
|
||||
context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph);
|
||||
context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph_dir);
|
||||
context.Args.AddLastArg(Arguments, options::OPT_include_spi_symbols);
|
||||
|
||||
@@ -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];
|
||||
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");
|
||||
|
||||
@@ -111,6 +111,10 @@
|
||||
|
||||
using namespace swift;
|
||||
|
||||
static std::vector<std::string>
|
||||
collectSupplementaryOutputPaths(ArrayRef<const char *> Args,
|
||||
options::ID OptionID);
|
||||
|
||||
static std::string displayName(StringRef MainExecutablePath) {
|
||||
std::string Name = llvm::sys::path::stem(MainExecutablePath).str();
|
||||
Name += " -frontend";
|
||||
@@ -724,16 +728,14 @@ static bool writeAPIDescriptorIfNeeded(CompilerInstance &Instance) {
|
||||
Instance.getOutputBackend());
|
||||
}
|
||||
|
||||
static bool performCompileStepsPostSILGen(CompilerInstance &Instance,
|
||||
std::unique_ptr<SILModule> SM,
|
||||
ModuleOrSourceFile MSF,
|
||||
const PrimarySpecificPaths &PSPs,
|
||||
int &ReturnValue,
|
||||
FrontendObserver *observer);
|
||||
static bool performCompileStepsPostSILGen(
|
||||
CompilerInstance &Instance, std::unique_ptr<SILModule> SM,
|
||||
ModuleOrSourceFile MSF, const PrimarySpecificPaths &PSPs, int &ReturnValue,
|
||||
FrontendObserver *observer, ArrayRef<const char *> CommandLineArgs);
|
||||
|
||||
bool swift::performCompileStepsPostSema(CompilerInstance &Instance,
|
||||
int &ReturnValue,
|
||||
FrontendObserver *observer) {
|
||||
bool swift::performCompileStepsPostSema(
|
||||
CompilerInstance &Instance, int &ReturnValue, FrontendObserver *observer,
|
||||
ArrayRef<const char *> CommandLineArgs) {
|
||||
const auto &Invocation = Instance.getInvocation();
|
||||
const FrontendOptions &opts = Invocation.getFrontendOptions();
|
||||
|
||||
@@ -778,7 +780,8 @@ bool swift::performCompileStepsPostSema(CompilerInstance &Instance,
|
||||
auto SM = performASTLowering(mod, Instance.getSILTypes(), SILOpts,
|
||||
&irgenOpts);
|
||||
return performCompileStepsPostSILGen(Instance, std::move(SM), mod, PSPs,
|
||||
ReturnValue, observer);
|
||||
ReturnValue, observer,
|
||||
CommandLineArgs);
|
||||
}
|
||||
|
||||
|
||||
@@ -797,7 +800,7 @@ bool swift::performCompileStepsPostSema(CompilerInstance &Instance,
|
||||
SILOpts, &irgenOpts);
|
||||
result |= performCompileStepsPostSILGen(Instance, std::move(SM),
|
||||
PrimaryFile, PSPs, ReturnValue,
|
||||
observer);
|
||||
observer, CommandLineArgs);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -814,7 +817,8 @@ bool swift::performCompileStepsPostSema(CompilerInstance &Instance,
|
||||
SILOptions SILOpts = getSILOptions(PSPs, emptyAuxPSPs);
|
||||
auto SM = performASTLowering(*SASTF, Instance.getSILTypes(), SILOpts);
|
||||
result |= performCompileStepsPostSILGen(Instance, std::move(SM), mod,
|
||||
PSPs, ReturnValue, observer);
|
||||
PSPs, ReturnValue, observer,
|
||||
CommandLineArgs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1230,9 +1234,9 @@ static bool performParseOnly(ModuleDecl &MainModule) {
|
||||
return MainModule.getASTContext().hadError();
|
||||
}
|
||||
|
||||
static bool performAction(CompilerInstance &Instance,
|
||||
int &ReturnValue,
|
||||
FrontendObserver *observer) {
|
||||
static bool performAction(CompilerInstance &Instance, int &ReturnValue,
|
||||
FrontendObserver *observer,
|
||||
ArrayRef<const char *> CommandLineArgs) {
|
||||
const auto &opts = Instance.getInvocation().getFrontendOptions();
|
||||
switch (Instance.getInvocation().getFrontendOptions().RequestedAction) {
|
||||
// MARK: Trivial Actions
|
||||
@@ -1322,7 +1326,8 @@ static bool performAction(CompilerInstance &Instance,
|
||||
Instance, observer, [&](CompilerInstance &Instance) {
|
||||
assert(FrontendOptions::doesActionGenerateSIL(opts.RequestedAction) &&
|
||||
"All actions not requiring SILGen must have been handled!");
|
||||
return performCompileStepsPostSema(Instance, ReturnValue, observer);
|
||||
return performCompileStepsPostSema(Instance, ReturnValue, observer,
|
||||
CommandLineArgs);
|
||||
});
|
||||
}
|
||||
case FrontendOptions::ActionType::EmitSILGen:
|
||||
@@ -1342,7 +1347,8 @@ static bool performAction(CompilerInstance &Instance,
|
||||
Instance, observer, [&](CompilerInstance &Instance) {
|
||||
assert(FrontendOptions::doesActionGenerateSIL(opts.RequestedAction) &&
|
||||
"All actions not requiring SILGen must have been handled!");
|
||||
return performCompileStepsPostSema(Instance, ReturnValue, observer);
|
||||
return performCompileStepsPostSema(Instance, ReturnValue, observer,
|
||||
CommandLineArgs);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1702,9 +1708,9 @@ static bool generateReproducer(CompilerInstance &Instance,
|
||||
/// \param Instance Will be reset after performIRGeneration when the verifier
|
||||
/// mode is NoVerify and there were no errors.
|
||||
/// \returns true on error
|
||||
static bool performCompile(CompilerInstance &Instance,
|
||||
int &ReturnValue,
|
||||
FrontendObserver *observer) {
|
||||
static bool performCompile(CompilerInstance &Instance, int &ReturnValue,
|
||||
FrontendObserver *observer,
|
||||
ArrayRef<const char *> CommandLineArgs) {
|
||||
const auto &Invocation = Instance.getInvocation();
|
||||
const auto &opts = Invocation.getFrontendOptions();
|
||||
const FrontendOptions::ActionType Action = opts.RequestedAction;
|
||||
@@ -1730,7 +1736,8 @@ static bool performCompile(CompilerInstance &Instance,
|
||||
return true;
|
||||
}() && "Only supports parsing .swift files");
|
||||
|
||||
bool hadError = performAction(Instance, ReturnValue, observer);
|
||||
bool hadError =
|
||||
performAction(Instance, ReturnValue, observer, CommandLineArgs);
|
||||
auto canIgnoreErrorForExit = [&Instance, &opts]() {
|
||||
return opts.AllowModuleWithCompilerErrors ||
|
||||
(opts.isTypeCheckAction() && Instance.downgradeInterfaceVerificationErrors());
|
||||
@@ -1777,11 +1784,11 @@ static bool serializeModuleSummary(SILModule *SM,
|
||||
|
||||
static GeneratedModule
|
||||
generateIR(const IRGenOptions &IRGenOpts, const TBDGenOptions &TBDOpts,
|
||||
std::unique_ptr<SILModule> SM,
|
||||
const PrimarySpecificPaths &PSPs,
|
||||
std::unique_ptr<SILModule> SM, const PrimarySpecificPaths &PSPs,
|
||||
StringRef OutputFilename, ModuleOrSourceFile MSF,
|
||||
llvm::GlobalVariable *&HashGlobal,
|
||||
ArrayRef<std::string> parallelOutputFilenames) {
|
||||
ArrayRef<std::string> parallelOutputFilenames,
|
||||
ArrayRef<std::string> parallelIROutputFilenames) {
|
||||
if (auto *SF = MSF.dyn_cast<SourceFile *>()) {
|
||||
return performIRGeneration(SF, IRGenOpts, TBDOpts,
|
||||
std::move(SM), OutputFilename, PSPs,
|
||||
@@ -1790,7 +1797,8 @@ generateIR(const IRGenOptions &IRGenOpts, const TBDGenOptions &TBDOpts,
|
||||
} else {
|
||||
return performIRGeneration(cast<ModuleDecl *>(MSF), IRGenOpts, TBDOpts,
|
||||
std::move(SM), OutputFilename, PSPs,
|
||||
parallelOutputFilenames, &HashGlobal);
|
||||
parallelOutputFilenames,
|
||||
parallelIROutputFilenames, &HashGlobal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1972,12 +1980,10 @@ static bool generateCode(CompilerInstance &Instance, StringRef OutputFilename,
|
||||
Instance.getStatsReporter());
|
||||
}
|
||||
|
||||
static bool performCompileStepsPostSILGen(CompilerInstance &Instance,
|
||||
std::unique_ptr<SILModule> SM,
|
||||
ModuleOrSourceFile MSF,
|
||||
const PrimarySpecificPaths &PSPs,
|
||||
int &ReturnValue,
|
||||
FrontendObserver *observer) {
|
||||
static bool performCompileStepsPostSILGen(
|
||||
CompilerInstance &Instance, std::unique_ptr<SILModule> SM,
|
||||
ModuleOrSourceFile MSF, const PrimarySpecificPaths &PSPs, int &ReturnValue,
|
||||
FrontendObserver *observer, ArrayRef<const char *> CommandLineArgs) {
|
||||
const auto &Invocation = Instance.getInvocation();
|
||||
const auto &opts = Invocation.getFrontendOptions();
|
||||
FrontendOptions::ActionType Action = opts.RequestedAction;
|
||||
@@ -2102,6 +2108,14 @@ static bool performCompileStepsPostSILGen(CompilerInstance &Instance,
|
||||
if (Action == FrontendOptions::ActionType::EmitSIL)
|
||||
return writeSIL(*SM, PSPs, Instance, Invocation.getSILOptions());
|
||||
|
||||
// Write extra SIL output if requested
|
||||
if (!PSPs.SupplementaryOutputs.SILOutputPath.empty()) {
|
||||
if (writeSIL(*SM, Instance.getMainModule(), Invocation.getSILOptions(),
|
||||
PSPs.SupplementaryOutputs.SILOutputPath,
|
||||
Instance.getOutputBackend()))
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(Action >= FrontendOptions::ActionType::Immediate &&
|
||||
"All actions not requiring IRGen must have been handled!");
|
||||
assert(Action != FrontendOptions::ActionType::REPL &&
|
||||
@@ -2141,10 +2155,28 @@ static bool performCompileStepsPostSILGen(CompilerInstance &Instance,
|
||||
StringRef OutputFilename = PSPs.OutputFilename;
|
||||
std::vector<std::string> ParallelOutputFilenames =
|
||||
opts.InputsAndOutputs.copyOutputFilenames();
|
||||
|
||||
// Collect IR output paths from command line arguments
|
||||
std::vector<std::string> ParallelIROutputFilenames =
|
||||
collectSupplementaryOutputPaths(CommandLineArgs,
|
||||
options::OPT_ir_output_path);
|
||||
|
||||
llvm::GlobalVariable *HashGlobal;
|
||||
auto IRModule =
|
||||
generateIR(IRGenOpts, Invocation.getTBDGenOptions(), std::move(SM), PSPs,
|
||||
OutputFilename, MSF, HashGlobal, ParallelOutputFilenames);
|
||||
OutputFilename, MSF, HashGlobal, ParallelOutputFilenames,
|
||||
ParallelIROutputFilenames);
|
||||
|
||||
// Write extra LLVM IR output if requested
|
||||
if (IRModule && !PSPs.SupplementaryOutputs.LLVMIROutputPath.empty()) {
|
||||
if (withOutputPath(Instance.getDiags(), Instance.getOutputBackend(),
|
||||
PSPs.SupplementaryOutputs.LLVMIROutputPath,
|
||||
[&](raw_ostream &out) -> bool {
|
||||
IRModule.getModule()->print(out, nullptr);
|
||||
return false;
|
||||
}))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Cancellation check after IRGen.
|
||||
if (Instance.isCancellationRequested())
|
||||
@@ -2265,6 +2297,32 @@ swift_ASTGen_printStaticBuildConfiguration(BridgedLangOptions cLangOpts);
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
static std::vector<std::string>
|
||||
collectSupplementaryOutputPaths(ArrayRef<const char *> Args,
|
||||
options::ID OptionID) {
|
||||
std::vector<std::string> paths;
|
||||
|
||||
for (size_t i = 0; i < Args.size(); ++i) {
|
||||
StringRef arg = Args[i];
|
||||
StringRef optionName;
|
||||
|
||||
if (OptionID == options::OPT_sil_output_path) {
|
||||
optionName = "-sil-output-path";
|
||||
} else if (OptionID == options::OPT_ir_output_path) {
|
||||
optionName = "-ir-output-path";
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg == optionName && i + 1 < Args.size()) {
|
||||
paths.push_back(Args[i + 1]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
int swift::performFrontend(ArrayRef<const char *> Args,
|
||||
const char *Argv0, void *MainAddr,
|
||||
FrontendObserver *observer) {
|
||||
@@ -2468,13 +2526,13 @@ int swift::performFrontend(ArrayRef<const char *> Args,
|
||||
// Run the first time without observer and discard return value;
|
||||
int ReturnValueTest = 0;
|
||||
(void)performCompile(*VerifyInstance, ReturnValueTest,
|
||||
/*observer*/ nullptr);
|
||||
/*observer*/ nullptr, Args);
|
||||
// Get the hashing output backend and free the compiler instance.
|
||||
HashBackend = VerifyInstance->getHashingBackend();
|
||||
}
|
||||
|
||||
int ReturnValue = 0;
|
||||
bool HadError = performCompile(*Instance, ReturnValue, observer);
|
||||
bool HadError = performCompile(*Instance, ReturnValue, observer, Args);
|
||||
|
||||
if (verifierEnabled) {
|
||||
DiagnosticEngine &diags = Instance->getDiags();
|
||||
|
||||
@@ -377,7 +377,8 @@ bool CompileInstance::performCompile(
|
||||
CI->addDiagnosticConsumer(DiagC);
|
||||
SWIFT_DEFER { CI->removeDiagnosticConsumer(DiagC); };
|
||||
int ReturnValue = 0;
|
||||
return performCompileStepsPostSema(*CI, ReturnValue, /*observer=*/nullptr);
|
||||
return performCompileStepsPostSema(*CI, ReturnValue, /*observer=*/nullptr,
|
||||
Args);
|
||||
}
|
||||
|
||||
bool CompileInstance::shouldCheckDependencies() const {
|
||||
|
||||
@@ -1773,6 +1773,37 @@ static void performParallelIRGeneration(IRGenDescriptor desc) {
|
||||
}
|
||||
}
|
||||
|
||||
// Write IR outputs if requested
|
||||
// In WMO mode, IR generation creates separate modules per source file
|
||||
auto irOutputFilenames = desc.parallelIROutputFilenames;
|
||||
|
||||
if (!irOutputFilenames.empty()) {
|
||||
auto IRIter = irOutputFilenames.begin();
|
||||
|
||||
for (auto *File : M->getFiles()) {
|
||||
auto nextSF = dyn_cast<SourceFile>(File);
|
||||
if (!nextSF)
|
||||
continue;
|
||||
|
||||
// Write IR output for this source file
|
||||
if (IRIter != irOutputFilenames.end()) {
|
||||
auto irOutputPath = *IRIter++;
|
||||
if (!irOutputPath.empty()) {
|
||||
CurrentIGMPtr IGM = irgen.getGenModule(nextSF);
|
||||
std::error_code EC;
|
||||
llvm::raw_fd_ostream irOut(irOutputPath, EC);
|
||||
if (!EC) {
|
||||
IGM->getModule()->print(irOut, nullptr);
|
||||
irOut.close();
|
||||
} else {
|
||||
Ctx.Diags.diagnose(SourceLoc(), diag::error_opening_output,
|
||||
irOutputPath, EC.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bail out if there are any errors.
|
||||
if (Ctx.hadError()) return;
|
||||
|
||||
@@ -1804,6 +1835,7 @@ GeneratedModule swift::performIRGeneration(
|
||||
const TBDGenOptions &TBDOpts, std::unique_ptr<SILModule> SILMod,
|
||||
StringRef ModuleName, const PrimarySpecificPaths &PSPs,
|
||||
ArrayRef<std::string> parallelOutputFilenames,
|
||||
ArrayRef<std::string> parallelIROutputFilenames,
|
||||
llvm::GlobalVariable **outModuleHash) {
|
||||
// Get a pointer to the SILModule to avoid a potential use-after-move.
|
||||
const auto *SILModPtr = SILMod.get();
|
||||
@@ -1811,7 +1843,7 @@ GeneratedModule swift::performIRGeneration(
|
||||
auto desc = IRGenDescriptor::forWholeModule(
|
||||
M, Opts, TBDOpts, SILOpts, SILModPtr->Types, std::move(SILMod),
|
||||
ModuleName, PSPs, /*symsToEmit*/ std::nullopt, parallelOutputFilenames,
|
||||
outModuleHash);
|
||||
parallelIROutputFilenames, outModuleHash);
|
||||
|
||||
if (Opts.shouldPerformIRGenerationInParallel() &&
|
||||
!parallelOutputFilenames.empty() &&
|
||||
|
||||
@@ -262,7 +262,8 @@ generateModule(const CompilerInstance &CI, std::unique_ptr<SILModule> SM) {
|
||||
// Lower the SIL module to LLVM IR
|
||||
auto GenModule = performIRGeneration(
|
||||
swiftModule, IRGenOpts, TBDOpts, std::move(SM),
|
||||
swiftModule->getName().str(), PSPs, ArrayRef<std::string>());
|
||||
swiftModule->getName().str(), PSPs, ArrayRef<std::string>(),
|
||||
/*parallelIROutputFilenames*/ ArrayRef<std::string>());
|
||||
|
||||
if (Context.hadError()) {
|
||||
return std::nullopt;
|
||||
|
||||
14
test/Frontend/ir-output-path.swift
Normal file
14
test/Frontend/ir-output-path.swift
Normal file
@@ -0,0 +1,14 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %target-swift-frontend -emit-object -ir-output-path %t/test.ll %s -o %t/test.o
|
||||
// RUN: %FileCheck -input-file %t/test.ll %s --check-prefix=IR-CHECK
|
||||
// RUN: test -f %t/test.o
|
||||
|
||||
// Test that -ir-output-path produces LLVM IR output alongside normal compilation
|
||||
|
||||
func testFunction() -> Int {
|
||||
return 42
|
||||
}
|
||||
|
||||
let _ = testFunction()
|
||||
|
||||
// IR-CHECK: @"$s4test0A8FunctionSiyF"
|
||||
46
test/Frontend/output-file-map-sil-ir.swift
Normal file
46
test/Frontend/output-file-map-sil-ir.swift
Normal file
@@ -0,0 +1,46 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
|
||||
// Test that SIL and IR files can be requested via output file map
|
||||
|
||||
// Test primary file compilation with both SIL and IR in file map
|
||||
// RUN: echo '{"%/s": {"sil": "%/t/primary.sil", "llvm-ir": "%/t/primary.ll"}, "%/S/../Driver/Inputs/main.swift": {"sil": "%/t/main.sil", "llvm-ir": "%/t/main.ll"}}' > %t/multi-map.json
|
||||
// RUN: %target-swift-frontend -emit-object -supplementary-output-file-map %t/multi-map.json -primary-file %/s %/S/../Driver/Inputs/main.swift -o %t/primary.o -module-name test
|
||||
// RUN: test -f %t/primary.sil && test -f %t/primary.ll && test -f %t/primary.o
|
||||
// RUN: test ! -f %t/main.sil && test ! -f %t/main.ll
|
||||
// RUN: %FileCheck -input-file %t/primary.sil %s --check-prefix=SIL-CHECK
|
||||
// RUN: %FileCheck -input-file %t/primary.ll %s --check-prefix=IR-CHECK
|
||||
|
||||
// Test switching primary files - same map, different primary file
|
||||
// RUN: %target-swift-frontend -emit-object -supplementary-output-file-map %t/multi-map.json -primary-file %/S/../Driver/Inputs/main.swift %/s -o %t/main-primary.o -module-name test
|
||||
// RUN: test -f %t/main.sil && test -f %t/main.ll && test -f %t/main-primary.o
|
||||
// RUN: %FileCheck -input-file %t/main.sil %s --check-prefix=MAIN-SIL-CHECK
|
||||
// RUN: %FileCheck -input-file %t/main.ll %s --check-prefix=MAIN-IR-CHECK
|
||||
|
||||
// Test partial file maps: SIL-only and IR-only in one test
|
||||
// RUN: echo '{"%/s": {"sil": "%/t/partial.sil", "llvm-ir": "%/t/partial.ll"}}' > %t/partial-map.json
|
||||
// RUN: %target-swift-frontend -emit-object -supplementary-output-file-map %t/partial-map.json %/s -o %t/partial.o -module-name test
|
||||
// RUN: test -f %t/partial.sil && test -f %t/partial.ll && test -f %t/partial.o
|
||||
// RUN: %FileCheck -input-file %t/partial.sil %s --check-prefix=SIL-CHECK
|
||||
// RUN: %FileCheck -input-file %t/partial.ll %s --check-prefix=IR-CHECK
|
||||
|
||||
func testFunction() -> Int {
|
||||
return 42
|
||||
}
|
||||
|
||||
func runTest() {
|
||||
_ = testFunction()
|
||||
}
|
||||
|
||||
// Function expected by main.swift
|
||||
func libraryFunction() {}
|
||||
|
||||
// For module-qualified access
|
||||
struct ThisModule {
|
||||
static func libraryFunction() {}
|
||||
}
|
||||
|
||||
// SIL-CHECK: sil hidden @$s4test0A8FunctionSiyF : $@convention(thin) () -> Int
|
||||
// IR-CHECK: @"$s4test0A8FunctionSiyF"
|
||||
|
||||
// MAIN-SIL-CHECK: sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32
|
||||
// MAIN-IR-CHECK: define{{.*}} i32 @main(
|
||||
12
test/Frontend/sil-output-path.swift
Normal file
12
test/Frontend/sil-output-path.swift
Normal file
@@ -0,0 +1,12 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %target-swift-frontend -emit-object -sil-output-path %t/test.sil %s -o %t/test.o
|
||||
// RUN: %FileCheck -input-file %t/test.sil %s --check-prefix=SIL-CHECK
|
||||
// RUN: test -f %t/test.o
|
||||
|
||||
// Test that -sil-output-path produces SIL output alongside normal compilation
|
||||
|
||||
func testFunction() -> Int {
|
||||
return 42
|
||||
}
|
||||
|
||||
// SIL-CHECK: sil hidden @$s4test0A8FunctionSiyF : $@convention(thin) () -> Int
|
||||
36
test/Frontend/wmo-supplementary-outputs.swift
Normal file
36
test/Frontend/wmo-supplementary-outputs.swift
Normal file
@@ -0,0 +1,36 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
|
||||
// Test WMO supplementary output functionality
|
||||
|
||||
// Test SIL consolidates, IR separates in multi-threaded WMO
|
||||
// RUN: %target-swift-frontend -wmo -num-threads 4 %S/../Driver/Inputs/main.swift %s -module-name=ThisModule -c -o %t/main.o -o %t/multi-threaded.o -sil-output-path %t/mt-wmo.sil -ir-output-path %t/main.ll -ir-output-path %t/multi-threaded.ll
|
||||
// RUN: test -f %t/mt-wmo.sil && test -f %t/main.ll && test -f %t/multi-threaded.ll && test -f %t/main.o && test -f %t/multi-threaded.o
|
||||
// RUN: %FileCheck -input-file %t/mt-wmo.sil %s --check-prefix=SIL-CHECK
|
||||
// RUN: %FileCheck -input-file %t/main.ll %s --check-prefix=IR-CHECK-MAIN
|
||||
// RUN: %FileCheck -input-file %t/multi-threaded.ll %s --check-prefix=IR-CHECK
|
||||
|
||||
// MARK: Single-threaded WMO tests - Both SIL and IR consolidate
|
||||
|
||||
// Test single-threaded WMO: both SIL and IR produce consolidated output
|
||||
// RUN: %target-swift-frontend -wmo %S/../Driver/Inputs/main.swift %s -module-name=ThisModule -c -o %t/st-main.o -sil-output-path %t/st-wmo.sil -ir-output-path %t/st-wmo.ll
|
||||
// RUN: test -f %t/st-wmo.sil && test -f %t/st-wmo.ll && test -f %t/st-main.o
|
||||
// RUN: %FileCheck -input-file %t/st-wmo.sil %s --check-prefix=SIL-CHECK
|
||||
// RUN: %FileCheck -input-file %t/st-wmo.ll %s --check-prefix=IR-CHECK
|
||||
|
||||
// MARK: WMO with supplementary output file maps - First entry consolidation
|
||||
|
||||
// Test file map consolidation: both SIL and IR use first entry naming with consolidated content
|
||||
// RUN: echo '{"%/S/../Driver/Inputs/main.swift": {"sil": "%/t/map.sil", "llvm-ir": "%/t/map.ll"}, "%/s": {"sil": "%/t/unused.sil", "llvm-ir": "%/t/unused.ll"}}' > %t/map.json
|
||||
// RUN: %target-swift-frontend -wmo %/S/../Driver/Inputs/main.swift %/s -module-name=ThisModule -c -o %t/map.o -supplementary-output-file-map %t/map.json
|
||||
// RUN: test -f %t/map.sil && test -f %t/map.ll && test -f %t/map.o
|
||||
// RUN: test ! -f %t/unused.sil && test ! -f %t/unused.ll
|
||||
// RUN: %FileCheck -input-file %t/map.sil %s --check-prefix=SIL-CHECK
|
||||
// RUN: %FileCheck -input-file %t/map.ll %s --check-prefix=IR-CHECK
|
||||
|
||||
// SIL-CHECK: sil {{.*}} @$s10ThisModule15libraryFunctionyyF
|
||||
|
||||
// IR-CHECK: @"$s10ThisModule15libraryFunctionyyF"
|
||||
|
||||
// IR-CHECK-MAIN: define{{.*}} i32 @main(
|
||||
|
||||
func libraryFunction() {}
|
||||
Reference in New Issue
Block a user