Skip to content

Conversation

rjmansfield
Copy link
Contributor

This change introduces two new frontend options that enable writing intermediate representations as additional outputs during compilation:

-sil-output-path : Outputs SIL to the specified path
-ir-output-path : Outputs LLVM IR to the specified path

These options can be useful for debugging and analysis tools workflows that need the IR forms without requiring separate compiler invocations.

rdar://160297898

@rjmansfield
Copy link
Contributor Author

@swift-ci please test

@aschwaighofer
Copy link
Contributor

Can you add tests showing this working for multi threaded wmo?

@rjmansfield
Copy link
Contributor Author

Can you add tests showing this working for multi threaded wmo?

Ah yeah, I see the issue in performParallelIRGeneration now. I guess in multithreaded WMO we'll need one path per source file then

Copy link
Contributor

@cachemeifyoucan cachemeifyoucan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added frontend logics LGTM. The test should be frontend test instead (direct swift-frontend invocation, rather than swift driver invocation, reasons below).

Driver code is optional since the legacy driver is deprecated. If you intended to make this a driver flag, please implement the logic in the new swift-driver.

@DougGregor
Copy link
Member

Can you add tests showing this working for multi threaded wmo?

Ah yeah, I see the issue in performParallelIRGeneration now. I guess in multithreaded WMO we'll need one path per source file then

Isn't there an IR linking phase, or does that not create a single IR module to print? It's going to be really tricky to deal with one path per source file in build systems when there is only a single object file that's emitted at the end.

Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but I'd like to have a solution to the multi-threaded WMO issue. That's a fairly common configuration.

HelpText<"Output semantic info of current module to <path>">;

def sil_output_path
: Separate<["-"], "sil-output-path">, MetaVarName<"<path>">,
Copy link
Contributor

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:

def SupplementaryOutput : OptionFlag;

@aschwaighofer
Copy link
Contributor

@DougGregor In multi-thread WMO, there will be one LLVM Module (.ll file) per source file and also one final product (.o/.bc) file per source file.

@DougGregor
Copy link
Member

@DougGregor In multi-thread WMO, there will be one LLVM Module (.ll file) per source file and also one final product (.o/.bc) file per source file.

That's tough for this feature, because our output file map is going to have just a single entry (since there's one output file, e.g., .o).

I have a very silly idea! In this case, can we print some split-file-like thing so that there's just one output .ll file, but we break it up with, e.g.,

; --- <source-file-name-1>.ll
first source file IR
; --- <source-file-name-2>.ll
second source file IR

@rjmansfield
Copy link
Contributor Author

Ok, I'll explore that. I have a patch that emits separate .ll files, but doesn't address the issue @DougGregor pointed out with supplementary output filemaps. I'll update the current diff in case it solicited further feedback.

@rjmansfield
Copy link
Contributor Author

@swift-ci please test

@rjmansfield rjmansfield force-pushed the sil-ir-extra-outputs branch from 3ad7231 to a17d73d Compare October 1, 2025 13:31
@rjmansfield
Copy link
Contributor Author

@swift-ci please smoke test

@rjmansfield
Copy link
Contributor Author

Please test with following pull request:

swiftlang/llvm-project#11558

@swift-ci Please test

@rjmansfield rjmansfield force-pushed the sil-ir-extra-outputs branch from a17d73d to 7c4614a Compare October 6, 2025 13:19
@rjmansfield
Copy link
Contributor Author

Please test with following pull request:

swiftlang/llvm-project#11558

@swift-ci Please test

…on 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
@rjmansfield rjmansfield force-pushed the sil-ir-extra-outputs branch from 7c4614a to ba0ce8a Compare October 6, 2025 19:46
@rjmansfield
Copy link
Contributor Author

Please test with following pull request:

swiftlang/llvm-project#11558

@swift-ci Please test

@rjmansfield
Copy link
Contributor Author

Please test with following pull request:

swiftlang/llvm-project#11558

@swift-ci Please smoke test

@rjmansfield
Copy link
Contributor Author

Just to mention the expected behaviour of this change from the commit message:

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.

sop.APIDescriptorOutputPath = (*apiDescriptorOutput)[moduleIndex];
sop.ConstValuesOutputPath = (*constValuesOutput)[moduleIndex];
sop.ModuleSemanticInfoOutputPath = (*moduleSemanticInfoOutput)[moduleIndex];
sop.YAMLOptRecordPath = (*optRecordOutput)[moduleIndex];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, not sure this is right for optimization record (...OptRecordPath) files.There should be one per source file/.o/llvm module.

Can you check that there will be two .yaml or two .bitstream files produced after this patch for the following test case:

% cat > test-save-bitstream/A.swift
public func A() -> Int {
  print(5)
  return 6
}
 % cat > test-save-bitstream/B.swift
public func B() -> Int {
  print("In B: \(7)")
  return 8
}
% cd test-save-bitstream
% ..../swift-macosx-arm64/bin/swiftc -wmo -num-threads 1 -v -c A.swift B.swift -save-optimization-record=bitstream
% ls -ltr
 main.opt.bitstream
 B.opt.bitstream
 % .../swift-macosx-arm64/bin/swiftc -wmo -num-threads 1 -v -c A.swift B.swift -save-optimization-record=yaml
 % ls -ltr
 main.opt.yaml
 B.opt.yaml

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it generates two separate files.

% ~/swift/build/Ninja-RelWithDebInfoAssert/swift-macosx-arm64/bin/swiftc -wmo -num-threads 1 -v -c A.swift B.swift -save-optimization-record=bitstream
Swift version 6.3-dev (LLVM 2673c29efb030de, Swift 5e4b1f6c2fc517b)
Target: arm64-apple-macosx26.0
/Users/ryan_mansfield/swift/build/Ninja-RelWithDebInfoAssert/swift-macosx-arm64/bin/swift-frontend -frontend -c A.swift B.swift -save-optimization-record=bitstream -save-optimization-record-path main.opt.bitstream -target arm64-apple-macosx26.0 -Xllvm -aarch64-use-tbi -enable-objc-interop -color-diagnostics -Xcc -fcolor-diagnostics -empty-abi-descriptor -no-auto-bridging-header-chaining -module-name main -in-process-plugin-server-path /Users/ryan_mansfield/swift/build/Ninja-RelWithDebInfoAssert/swift-macosx-arm64/lib/swift/host/libSwiftInProcPluginServer.dylib -plugin-path /Users/ryan_mansfield/swift/build/Ninja-RelWithDebInfoAssert/swift-macosx-arm64/lib/swift/host/plugins -plugin-path /Users/ryan_mansfield/swift/build/Ninja-RelWithDebInfoAssert/swift-macosx-arm64/local/lib/swift/host/plugins -num-threads 1 -o A.o -o B.o
ryan_mansfield@MacBook-Pro-2 swift % ls -1 *.bitstream                                                                                                                                   
B.opt.bitstream
main.opt.bitstream

% ~/swift/build/Ninja-RelWithDebInfoAssert/swift-macosx-arm64/bin/swiftc -wmo -num-threads 1 -v -c A.swift B.swift -save-optimization-record=yaml     
Swift version 6.3-dev (LLVM 2673c29efb030de, Swift 5e4b1f6c2fc517b)
Target: arm64-apple-macosx26.0
/Users/ryan_mansfield/swift/build/Ninja-RelWithDebInfoAssert/swift-macosx-arm64/bin/swift-frontend -frontend -c A.swift B.swift -save-optimization-record=yaml -save-optimization-record-path main.opt.yaml -target arm64-apple-macosx26.0 -Xllvm -aarch64-use-tbi -enable-objc-interop -color-diagnostics -Xcc -fcolor-diagnostics -empty-abi-descriptor -no-auto-bridging-header-chaining -module-name main -in-process-plugin-server-path /Users/ryan_mansfield/swift/build/Ninja-RelWithDebInfoAssert/swift-macosx-arm64/lib/swift/host/libSwiftInProcPluginServer.dylib -plugin-path /Users/ryan_mansfield/swift/build/Ninja-RelWithDebInfoAssert/swift-macosx-arm64/lib/swift/host/plugins -plugin-path /Users/ryan_mansfield/swift/build/Ninja-RelWithDebInfoAssert/swift-macosx-arm64/local/lib/swift/host/plugins -num-threads 1 -o A.o -o B.o
 % ls -1 *.yaml                                                                                                                                   
B.opt.yaml
main.opt.yaml

Interestingly the swiftc 6.2 I have installed generates only main.opt.bitstream or main.opt.yaml

Copy link
Contributor

Choose a reason for hiding this comment

The 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

Yes, I fixed this behavior recently.

Copy link
Member

@DougGregor DougGregor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking great to me now, thank you!

Copy link
Contributor

@cachemeifyoucan cachemeifyoucan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM as well.

@rjmansfield
Copy link
Contributor Author

Thanks for the reviews. I should mention that this llvm-project change is needed here: swiftlang/llvm-project#11558 as this patch adds a parameter to performIRGeneration. Also I don't have write access so when all approved, I'll need someone to merge both changes on my behalf. TIA

BTW, The proposed swift-driver change that uses these options is here: swiftlang/swift-driver#1995

@DougGregor DougGregor merged commit ce5b1a1 into swiftlang:main Oct 10, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants