Skip to content

Commit c792635

Browse files
committed
Add -sil-output-dir and -ir-output-dir driver options
This enables generating SIL and LLVM IR files during normal compilation using the frontend -sil-output-path and -ir-output-path flags. Supports both single-file and WMO modes with file map integration. rdar://160297898
1 parent 049acb8 commit c792635

File tree

6 files changed

+322
-2
lines changed

6 files changed

+322
-2
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,12 @@ public struct Driver {
389389
/// Path to the Objective-C generated header.
390390
let objcGeneratedHeaderPath: VirtualPath.Handle?
391391

392+
/// Path to the SIL output file.
393+
let silOutputPath: VirtualPath.Handle?
394+
395+
/// Path to the LLVM IR output file.
396+
let llvmIROutputPath: VirtualPath.Handle?
397+
392398
/// Path to the loaded module trace file.
393399
let loadedModuleTracePath: VirtualPath.Handle?
394400

@@ -1296,6 +1302,11 @@ public struct Driver {
12961302
emitModuleSeparately: emitModuleSeparately,
12971303
outputFileMap: self.outputFileMap,
12981304
moduleName: moduleOutputInfo.name)
1305+
// SIL and IR outputs are handled through directory options and file maps
1306+
// rather than single output paths, so we set these to nil to enable
1307+
// the supplementary output path logic in FrontendJobHelpers
1308+
self.silOutputPath = nil
1309+
self.llvmIROutputPath = nil
12991310

13001311
if let loadedModuleTraceEnvVar = env["SWIFT_LOADED_MODULE_TRACE_FILE"] {
13011312
self.loadedModuleTracePath = try VirtualPath.intern(path: loadedModuleTraceEnvVar)
@@ -3871,6 +3882,18 @@ extension Driver {
38713882
return try VirtualPath.intern(path: moduleName.appendingFileTypeExtension(type))
38723883
}
38733884

3885+
static func hasFileMapEntry(outputFileMap: OutputFileMap?, fileType: FileType) -> Bool {
3886+
guard let outputFileMap = outputFileMap else { return false }
3887+
3888+
// Check if any input has this file type in the output file map
3889+
for inputFile in outputFileMap.entries.keys {
3890+
if outputFileMap.entries[inputFile]?[fileType] != nil {
3891+
return true
3892+
}
3893+
}
3894+
return false
3895+
}
3896+
38743897
/// Determine if the build system has created a Project/ directory for auxiliary outputs.
38753898
static func computeProjectDirectoryPath(moduleOutputPath: VirtualPath.Handle?,
38763899
fileSystem: FileSystem) -> VirtualPath.Handle? {

Sources/SwiftDriver/Driver/OutputFileMap.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,11 @@ public struct OutputFileMap: Hashable, Codable {
172172
}
173173
return result
174174
}
175+
176+
/// Check if the output file map has any entries for the given file type
177+
public func hasEntries(for fileType: FileType) -> Bool {
178+
return entries.values.contains { $0[fileType] != nil }
179+
}
175180
}
176181

177182
/// Struct for loading the JSON file from disk.

Sources/SwiftDriver/Jobs/CompileJob.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ extension Driver {
279279
moduleOutputInfo: self.moduleOutputInfo,
280280
moduleOutputPaths: self.moduleOutputPaths,
281281
includeModuleTracePath: emitModuleTrace,
282-
indexFilePaths: indexFilePaths)
282+
indexFilePaths: indexFilePaths,
283+
allInputs: inputs)
283284

284285
// Forward migrator flags.
285286
try commandLine.appendLast(.apiDiffDataFile, from: &parsedOptions)

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,16 +667,59 @@ extension Driver {
667667
moduleOutputInfo: ModuleOutputInfo,
668668
moduleOutputPaths: SupplementalModuleTargetOutputPaths,
669669
includeModuleTracePath: Bool,
670-
indexFilePaths: [TypedVirtualPath]) throws -> [TypedVirtualPath] {
670+
indexFilePaths: [TypedVirtualPath],
671+
allInputs: [TypedVirtualPath] = []) throws -> [TypedVirtualPath] {
671672
var flaggedInputOutputPairs: [(flag: String, input: TypedVirtualPath?, output: TypedVirtualPath)] = []
672673

674+
/// Generate directory-based output path for supplementary outputs
675+
func generateSupplementaryOutputPath(for input: TypedVirtualPath, outputType: FileType, directory: String) throws -> TypedVirtualPath {
676+
let inputBasename = input.file.basenameWithoutExt
677+
let fileExtension = outputType == .sil ? "sil" : "ll"
678+
let filename = "\(inputBasename).\(fileExtension)"
679+
let individualPath = try VirtualPath(path: directory).appending(component: filename)
680+
let outputPath = individualPath.intern()
681+
return TypedVirtualPath(file: outputPath, type: outputType)
682+
}
683+
684+
/// Process inputs for supplementary output generation (SIL/IR)
685+
func processInputsForSupplementaryOutput(inputs: [TypedVirtualPath], outputType: FileType, flag: String, directory: String?) throws {
686+
for inputFile in inputs {
687+
// Check output file map first, then fall back to directory-based generation
688+
if let outputFileMapPath = try outputFileMap?.existingOutput(inputFile: inputFile.fileHandle, outputType: outputType) {
689+
flaggedInputOutputPairs.append((flag: flag, input: inputFile, output: TypedVirtualPath(file: outputFileMapPath, type: outputType)))
690+
} else if let directory = directory {
691+
let outputPath = try generateSupplementaryOutputPath(for: inputFile, outputType: outputType, directory: directory)
692+
flaggedInputOutputPairs.append((flag: flag, input: inputFile, output: outputPath))
693+
}
694+
}
695+
}
696+
673697
/// Add output of a particular type, if needed.
674698
func addOutputOfType(
675699
outputType: FileType,
676700
finalOutputPath: VirtualPath.Handle?,
677701
input: TypedVirtualPath?,
678702
flag: String
679703
) throws {
704+
// Handle directory-based options and file maps for SIL and LLVM IR when finalOutputPath is nil
705+
if finalOutputPath == nil && (outputType == .sil || outputType == .llvmIR) {
706+
let directoryOption: Option = outputType == .sil ? .silOutputDir : .irOutputDir
707+
let directory = parsedOptions.getLastArgument(directoryOption)?.asSingle
708+
let hasFileMapEntries = outputFileMap?.hasEntries(for: outputType) ?? false
709+
710+
if directory != nil || hasFileMapEntries {
711+
let inputsToProcess: [TypedVirtualPath]
712+
if compilerMode.usesPrimaryFileInputs {
713+
inputsToProcess = input.map { [$0] } ?? []
714+
} else {
715+
inputsToProcess = allInputs
716+
}
717+
718+
try processInputsForSupplementaryOutput(inputs: inputsToProcess, outputType: outputType, flag: flag, directory: directory)
719+
return
720+
}
721+
}
722+
680723
// If there is no final output, there's nothing to do.
681724
guard let finalOutputPath = finalOutputPath else { return }
682725

@@ -763,6 +806,18 @@ extension Driver {
763806
finalOutputPath: serializedDiagnosticsFilePath,
764807
input: input,
765808
flag: "-serialize-diagnostics-path")
809+
810+
try addOutputOfType(
811+
outputType: .sil,
812+
finalOutputPath: silOutputPath,
813+
input: input,
814+
flag: "-sil-output-path")
815+
816+
try addOutputOfType(
817+
outputType: .llvmIR,
818+
finalOutputPath: llvmIROutputPath,
819+
input: input,
820+
flag: "-ir-output-path")
766821
}
767822

768823
if compilerMode.usesPrimaryFileInputs {

Sources/SwiftOptions/Options.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,7 @@ extension Option {
655655
public static let Isystem: Option = Option("-Isystem", .separate, attributes: [.frontend, .synthesizeInterface, .argumentIsPath], helpText: "Add directory to the system import search path")
656656
public static let I: Option = Option("-I", .joinedOrSeparate, attributes: [.frontend, .synthesizeInterface, .argumentIsPath], helpText: "Add directory to the import search path")
657657
public static let i: Option = Option("-i", .flag, group: .modes)
658+
public static let irOutputDir: Option = Option("-ir-output-dir", .separate, attributes: [.argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "<dir>", helpText: "Output LLVM IR files to directory <dir> as additional output during compilation")
658659
public static let json: Option = Option("-json", .flag, attributes: [.noDriver], helpText: "Print output in JSON format.")
659660
public static let json_: Option = Option("--json", .flag, alias: Option.json, attributes: [.noDriver], helpText: "Print output in JSON format.")
660661
public static let j: Option = Option("-j", .joinedOrSeparate, attributes: [.doesNotAffectIncrementalBuild], metaVar: "<n>", helpText: "Number of commands to execute in parallel")
@@ -864,6 +865,7 @@ extension Option {
864865
public static let silDebugSerialization: Option = Option("-sil-debug-serialization", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Do not eliminate functions in Mandatory Inlining/SILCombine dead functions. (for debugging only)")
865866
public static let silInlineCallerBenefitReductionFactor: Option = Option("-sil-inline-caller-benefit-reduction-factor", .separate, attributes: [.helpHidden, .frontend, .noDriver], metaVar: "<2>", helpText: "Controls the aggressiveness of performance inlining in -Osize mode by reducing the base benefits of a caller (lower value permits more inlining!)")
866867
public static let silInlineThreshold: Option = Option("-sil-inline-threshold", .separate, attributes: [.helpHidden, .frontend, .noDriver], metaVar: "<50>", helpText: "Controls the aggressiveness of performance inlining")
868+
public static let silOutputDir: Option = Option("-sil-output-dir", .separate, attributes: [.argumentIsPath, .supplementaryOutput, .cacheInvariant], metaVar: "<dir>", helpText: "Output SIL files to directory <dir> as additional output during compilation")
867869
public static let silOwnershipVerifyAll: Option = Option("-sil-ownership-verify-all", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Verify ownership after each transform")
868870
public static let silStopOptznsBeforeLoweringOwnership: Option = Option("-sil-stop-optzns-before-lowering-ownership", .flag, attributes: [.helpHidden, .frontend, .noDriver], helpText: "Stop optimizing at SIL time before we lower ownership from SIL. Intended only for SIL ossa tests")
869871
public static let silUnrollThreshold: Option = Option("-sil-unroll-threshold", .separate, attributes: [.helpHidden, .frontend, .noDriver], metaVar: "<250>", helpText: "Controls the aggressiveness of loop unrolling")
@@ -1610,6 +1612,7 @@ extension Option {
16101612
Option.importPch,
16111613
Option.importPrescan,
16121614
Option.importUnderlyingModule,
1615+
Option.irOutputDir,
16131616
Option.inPlace,
16141617
Option.inProcessPluginServerPath,
16151618
Option.includeSpiSymbols,
@@ -1849,6 +1852,7 @@ extension Option {
18491852
Option.silDebugSerialization,
18501853
Option.silInlineCallerBenefitReductionFactor,
18511854
Option.silInlineThreshold,
1855+
Option.silOutputDir,
18521856
Option.silOwnershipVerifyAll,
18531857
Option.silStopOptznsBeforeLoweringOwnership,
18541858
Option.silUnrollThreshold,

0 commit comments

Comments
 (0)