Skip to content

Conversation

chiragramani
Copy link

@chiragramani chiragramani commented Sep 17, 2025

This PR introduces three new instrumentation flags and plumbs them through to IRGen:

  1. -ir-profile-generate - enable IR-level instrumentation.
  2. -cs-profile-generate - enable context-sensitive IR-level instrumentation.
  3. -ir-profile-use - IR-level PGO input profdata file to enable profile-guided optimization (both IRPGO and CSIRPGO)

Context: https://forums.swift.org/t/ir-level-pgo-instrumentation-in-swift/82123

Swift-driver PR: swiftlang/swift-driver#1992

Tests and validation:
This PR includes ir level verification tests, also checks few edge-cases when -ir-profile-use supplied profile is either missing or is an invalid IR profile.

However, for argument validation, linking, and generating IR profiles that can later be consumed by -cs-profile-generate, I’ll need corresponding swift-driver changes. Those changes are being tracked in swiftlang/swift-driver#1992

The plan is:

  1. Land this PR first.
  2. Then update swift-driver with the required changes.
  3. Then add the remaining set of test cases in this repo.

Open Questions (Reviewer Input Requested)

1. Naming:
Is -ir-profile-generate a good flag name?
-cs-profile-generate pairs nicely with Clang’s -fcs-profile-generate. However, -ir-profile-generate feels closer to Clang's -fprofile-generate but -profile-generate already exists in Swift as a frontend level and not IR level. Should it stay as is, or is there a clearer alternative?

2. Missing LLVM runtime + Blocked on tests

At the moment, I haven’t added tests because they currently fail locally due to missing LLVM runtime symbols. This is expected, since the required runtime linking through libclang APIs will be handled in swift-driver(swiftlang/swift-driver#1992). However, swift-driver itself depends on these new options being available in Swift.

Does that mean the correct order of the PRs should be the following?

1. Create a PR in Swift that introduces just the new options, and land that PR first.
2. Land the corresponding swift-driver PR to handle runtime linking,
3. Then follow up with a Swift PR adding the full functionality and tests?

~~

@aschwaighofer
Copy link
Contributor

I don't have a strong opinion on naming. My feeling is that context sensitive and non context sensitive LLVM IR level instrumentation should follow a similar pattern to make clear they are related. We have already deviated from clang's naming scheme -- that is, clang's fprofile-generate(llvm ir level instrumentation) is not equal swift's -profile-generate ("AST" based insertion of instrumentation). -ir-profile-generate and -cs-ir-profile-generate would make it clearer that they are related.

@aschwaighofer
Copy link
Contributor

Does that mean the correct order of the PRs should be the following?
Create a PR in Swift that introduces just the new options, and land that PR first.
Land the corresponding swift-driver PR to handle runtime linking,
Then follow up with a Swift PR adding the full functionality and tests?

You could add LLVM IR level tests that test the functionality of this PR (look at most tests in test/IRGen) and merge this PR first. Then you could land the corresponding swift-driver PR. Then you could at a swiftlang/swift repo commit to add execution tests.

// RUN: %target-swift-frontend -ir-profile-generate -emit-ir %s | %FileCheck %s

// CHECK: define @mangleNameOfA
// CHECK: the llvm ir you expect to see (calls to profile runtime function/intrinsics)
public func a(_ cond: Bool) {
  if cond {
     b()
  } else {
    c()
  }
}

Preferably, you would as .sil tests rather than .swift tests as that would make them more "stable".

That would look something like the following (I ran swiftc -emit-sil test.swift and edited the output a little bit):

// RUN: %target-swift-frontend -ir-profile-generate -emit-ir %s | %FileCheck %s

sil_stage canonical

import Builtin
import Swift
import SwiftShims

sil @b : $@convention(thin) () -> ()

sil @c : $@convention(thin) () -> ()

// CHECK: define {{.*}} void @a
// CHECK: the llvm ir you expect to see (calls to profile runtime function/intrinsics)

sil @a : $@convention(thin) (Bool) -> () {
bb0(%0 : $Bool):
  %2 = struct_extract %0, #Bool._value
  cond_br %2, bb1, bb2

bb1:
  // function_ref b()
  %4 = function_ref @b : $@convention(thin) () -> ()
  %5 = apply %4() : $@convention(thin) () -> ()
  br bb3 

bb2:
  // function_ref c()
  %7 = function_ref @c: $@convention(thin) () -> ()
  %8 = apply %7() : $@convention(thin) () -> ()
  br bb3 

bb3:
  %10 = tuple ()
  return %10
}

@chiragramani
Copy link
Author

Preferably, you would as .sil tests rather than .swift tests as that would make them more "stable".

You could add LLVM IR level tests that test the functionality of this PR (look at most tests in test/IRGen) and merge this PR first. Then you could land the corresponding swift-driver PR. Then you could at a swiftlang/swift repo commit to add execution tests.

Thanks for the guidance here, it definitely helped me understand things better!

Added tests
@chiragramani
Copy link
Author

Hi @aschwaighofer, I've added the tests based on your suggestions. When you have a moment, could you please review the changes and share your feedback? Thanks

@chiragramani
Copy link
Author

@kavon would love to hear your perspective on this PR as well, when you have a moment.

@chiragramani
Copy link
Author

@swift-ci test

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.

3 participants