Skip to content

Commit acbb3b0

Browse files
committed
Add ir and cs instrumentation options
1 parent 83f8460 commit acbb3b0

File tree

9 files changed

+702
-21
lines changed

9 files changed

+702
-21
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3334,24 +3334,87 @@ extension Driver {
33343334
}
33353335
}
33363336

3337+
static private func validateProfilingGenerateArgs(
3338+
_ parsedOptions: inout ParsedOptions,
3339+
diagnosticEngine: DiagnosticsEngine
3340+
) {
3341+
let genFlags: [Option] = [
3342+
.profileGenerate,
3343+
.irProfileGenerate,
3344+
.csProfileGenerate,
3345+
.csProfileGenerateEq,
3346+
]
3347+
3348+
var providedGen = genFlags.filter { parsedOptions.hasArgument($0) }
3349+
if parsedOptions.hasArgument(.csProfileGenerate),
3350+
parsedOptions.hasArgument(.csProfileGenerateEq)
3351+
{
3352+
// If both forms were specified, report a clear conflict.
3353+
diagnosticEngine.emit(
3354+
.error(Error.conflictingOptions(.csProfileGenerate, .csProfileGenerateEq)),
3355+
location: nil
3356+
)
3357+
providedGen.removeAll { $0 == .csProfileGenerateEq }
3358+
}
3359+
3360+
guard providedGen.count >= 2 else { return }
3361+
for i in 1..<providedGen.count {
3362+
let error = Error.conflictingOptions(providedGen[i - 1], providedGen[i])
3363+
diagnosticEngine.emit(.error(error), location: nil)
3364+
}
3365+
}
3366+
3367+
static private func validateProfilingUseArgs(
3368+
_ parsedOptions: inout ParsedOptions,
3369+
diagnosticEngine: DiagnosticsEngine
3370+
) {
3371+
let conflictingGenFlags: [Option] = [
3372+
.profileGenerate,
3373+
.irProfileGenerate,
3374+
]
3375+
let useProfArgs: [Option] = [
3376+
.profileUse,
3377+
.profileSampleUse,
3378+
]
3379+
let providedUse = useProfArgs.filter { parsedOptions.hasArgument($0) }
3380+
guard !providedUse.isEmpty else { return }
3381+
3382+
// At most one *use* option allowed
3383+
if providedUse.count > 1 {
3384+
for i in 0..<(providedUse.count - 1) {
3385+
for j in (i + 1)..<providedUse.count {
3386+
diagnosticEngine.emit(
3387+
.error(Error.conflictingOptions(providedUse[i], providedUse[j])),
3388+
location: nil
3389+
)
3390+
}
3391+
}
3392+
}
3393+
3394+
// If no generate flags, we're good.
3395+
let providedGen = conflictingGenFlags.filter { parsedOptions.hasArgument($0) }
3396+
guard !providedGen.isEmpty else { return }
3397+
3398+
// We already diagnosed if the user passed more than one "use" option
3399+
// (e.g. both `-profile-use` and `-profile-sample-use`). To avoid
3400+
// spamming diagnostics, we now treat the first provided "use" flag
3401+
// as the canonical representative.
3402+
let canonicalUse = providedUse[0]
3403+
3404+
// Generate vs Use are mutually exclusive
3405+
for g in providedGen {
3406+
diagnosticEngine.emit(.error(Error.conflictingOptions(g, canonicalUse)), location: nil)
3407+
}
3408+
}
3409+
3410+
33373411
static func validateProfilingArgs(_ parsedOptions: inout ParsedOptions,
33383412
fileSystem: FileSystem,
33393413
workingDirectory: AbsolutePath?,
33403414
diagnosticEngine: DiagnosticsEngine) {
3341-
let conflictingProfArgs: [Option] = [.profileGenerate,
3342-
.profileUse,
3343-
.profileSampleUse]
3344-
33453415
// Find out which of the mutually exclusive profiling arguments were provided.
3346-
let provided = conflictingProfArgs.filter { parsedOptions.hasArgument($0) }
3347-
3348-
// If there's at least two of them, there's a conflict.
3349-
if provided.count >= 2 {
3350-
for i in 1..<provided.count {
3351-
let error = Error.conflictingOptions(provided[i-1], provided[i])
3352-
diagnosticEngine.emit(.error(error), location: nil)
3353-
}
3354-
}
3416+
validateProfilingGenerateArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
3417+
validateProfilingUseArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
33553418

33563419
// Ensure files exist for the given paths.
33573420
func checkForMissingProfilingData(_ profileDataArgs: [String]) {

Sources/SwiftDriver/Jobs/DarwinToolchain+LinkerSupport.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,7 @@ extension DarwinToolchain {
258258
fileSystem: fileSystem
259259
)
260260

261-
if parsedOptions.hasArgument(.profileGenerate) {
262-
commandLine.appendFlag("-fprofile-generate")
263-
}
261+
commandLine.appendFlags(mapInstrumentationTypeToClangArgs(from: &parsedOptions))
264262

265263
// These custom arguments should be right before the object file at the
266264
// end.

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,9 @@ extension Driver {
243243
try commandLine.appendLast(.RpassMissedEQ, from: &parsedOptions)
244244
try commandLine.appendLast(.suppressWarnings, from: &parsedOptions)
245245
try commandLine.appendLast(.profileGenerate, from: &parsedOptions)
246+
try commandLine.appendLast(.irProfileGenerate, from: &parsedOptions)
247+
try commandLine.appendLast(.csProfileGenerate, from: &parsedOptions)
248+
try commandLine.appendLast(.csProfileGenerateEq, from: &parsedOptions)
246249
try commandLine.appendLast(.profileUse, from: &parsedOptions)
247250
try commandLine.appendLast(.profileCoverageMapping, from: &parsedOptions)
248251
try commandLine.appendLast(.debugInfoForProfiling, from: &parsedOptions)

Sources/SwiftDriver/Jobs/GenericUnixToolchain+LinkerSupport.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ extension GenericUnixToolchain {
303303
}
304304
}
305305

306-
if parsedOptions.hasArgument(.profileGenerate) {
306+
if needsInstrumentedProfile(from: &parsedOptions) {
307307
let environment = (targetTriple.environment == .android) ? "-android" : ""
308308
let libProfile = VirtualPath.lookup(targetInfo.runtimeResourcePath.path)
309309
.appending(components: "clang", "lib", targetTriple.osNameUnversioned,

Sources/SwiftDriver/Jobs/WebAssemblyToolchain+LinkerSupport.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ extension WebAssemblyToolchain {
176176
commandLine.appendFlag("-fsanitize=\(sanitizerNames)")
177177
}
178178

179-
if parsedOptions.hasArgument(.profileGenerate) {
179+
if needsInstrumentedProfile(from: &parsedOptions) {
180180
let libProfile = VirtualPath.lookup(targetInfo.runtimeResourcePath.path)
181181
.appending(components: "clang", "lib", targetTriple.osName,
182182
"libclang_rt.profile-\(targetTriple.archName).a")

Sources/SwiftDriver/Jobs/WindowsToolchain+LinkerSupport.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ extension WindowsToolchain {
5050
// for now, which supports the behavior via a flag.
5151
// TODO: Once we've changed coverage to no longer rely on emitting
5252
// duplicate weak symbols (rdar://131295678), we can remove this.
53-
if parsedOptions.hasArgument(.profileGenerate) { return true }
53+
if needsInstrumentedProfile(from: &parsedOptions) { return true }
5454

5555
return false
5656
}()
@@ -228,11 +228,12 @@ extension WindowsToolchain {
228228
commandLine.appendFlag("-fsanitize=\(sanitize)")
229229
}
230230

231-
if parsedOptions.contains(.profileGenerate) {
231+
if needsInstrumentedProfile(from: &parsedOptions) {
232232
assert(bForceLLD,
233233
"LLD is currently required for profiling (rdar://131295678)")
234234

235-
commandLine.appendFlag("-fprofile-generate")
235+
commandLine.appendFlags(mapInstrumentationTypeToClangArgs(from: &parsedOptions))
236+
236237
// FIXME(rdar://131295678): Currently profiling requires the ability to
237238
// emit duplicate weak symbols. Assume we're using lld and pass
238239
// `-lld-allow-duplicate-weak` to enable this behavior.

Sources/SwiftDriver/Toolchains/Toolchain.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,32 @@ extension Toolchain {
376376
}
377377
return clangArg
378378
}
379+
380+
internal func mapInstrumentationTypeToClangArgs(from options: inout ParsedOptions) -> [String] {
381+
var args: [String] = []
382+
383+
if options.contains(.profileGenerate) || options.contains(.irProfileGenerate) {
384+
args.append("-fprofile-generate")
385+
}
386+
387+
if options.contains(.csProfileGenerate) {
388+
args.append("-fcs-profile-generate")
389+
}
390+
391+
if options.contains(.csProfileGenerateEq),
392+
let path = options.getLastArgument(.csProfileGenerateEq)?.asSingle {
393+
args.append("-fcs-profile-generate=\(path)")
394+
}
395+
396+
return args
397+
}
398+
399+
internal func needsInstrumentedProfile(from parsedOptions: inout ParsedOptions) -> Bool {
400+
parsedOptions.contains(.profileGenerate) ||
401+
parsedOptions.contains(.irProfileGenerate) ||
402+
parsedOptions.contains(.csProfileGenerate) ||
403+
parsedOptions.contains(.csProfileGenerateEq)
404+
}
379405
}
380406

381407
@_spi(Testing) public enum ToolchainError: Swift.Error {

Sources/SwiftOptions/Options.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,9 @@ extension Option {
804804
public static let printZeroStats: Option = Option("-print-zero-stats", .flag, attributes: [.helpHidden, .frontend], helpText: "Prints all stats even if they are zero")
805805
public static let profileCoverageMapping: Option = Option("-profile-coverage-mapping", .flag, attributes: [.frontend, .noInteractive], helpText: "Generate coverage data for use with profiled execution counts")
806806
public static let profileGenerate: Option = Option("-profile-generate", .flag, attributes: [.frontend, .noInteractive], helpText: "Generate instrumented code to collect execution counts")
807+
public static let irProfileGenerate: Option = Option("-ir-profile-generate", .flag, attributes: [.frontend, .noInteractive], helpText: "Generate instrumented code to collect execution counts into default.profraw (overridden by LLVM_PROFILE_FILE env var)")
808+
public static let csProfileGenerate: Option = Option("-cs-profile-generate", .flag, attributes: [.frontend, .noInteractive], helpText: "Generate instrumented code to collect context sensitive execution counts into default.profraw (overridden by LLVM_PROFILE_FILE env var)")
809+
public static let csProfileGenerateEq: Option = Option("-cs-profile-generate=", .joined, attributes: [.frontend, .noInteractive, .argumentIsPath], metaVar: "<dir>", helpText: "Generate instrumented code to collect context sensitive execution counts into <directory>/default.profraw (overridden by LLVM_PROFILE_FILE env var)")
807810
public static let profileSampleUse: Option = Option("-profile-sample-use=", .joined, attributes: [.frontend, .noInteractive, .argumentIsPath], metaVar: "<profile data>", helpText: "Supply sampling-based profiling data from llvm-profdata to enable profile-guided optimization")
808811
public static let profileStatsEntities: Option = Option("-profile-stats-entities", .flag, attributes: [.helpHidden, .frontend], helpText: "Profile changes to stats in -stats-output-dir, subdivided by source entity")
809812
public static let profileStatsEvents: Option = Option("-profile-stats-events", .flag, attributes: [.helpHidden, .frontend], helpText: "Profile changes to stats in -stats-output-dir")
@@ -1815,6 +1818,9 @@ extension Option {
18151818
Option.printZeroStats,
18161819
Option.profileCoverageMapping,
18171820
Option.profileGenerate,
1821+
Option.irProfileGenerate,
1822+
Option.csProfileGenerate,
1823+
Option.csProfileGenerateEq,
18181824
Option.profileSampleUse,
18191825
Option.profileStatsEntities,
18201826
Option.profileStatsEvents,

0 commit comments

Comments
 (0)