Skip to content

Commit 5c8c159

Browse files
committed
Add ir and cs instrumentation options
1 parent 16ca733 commit 5c8c159

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
@@ -3307,24 +3307,87 @@ extension Driver {
33073307
}
33083308
}
33093309

3310+
static private func validateProfilingGenerateArgs(
3311+
_ parsedOptions: inout ParsedOptions,
3312+
diagnosticEngine: DiagnosticsEngine
3313+
) {
3314+
let genFlags: [Option] = [
3315+
.profileGenerate,
3316+
.irProfileGenerate,
3317+
.csProfileGenerate,
3318+
.csProfileGenerateEq,
3319+
]
3320+
3321+
var providedGen = genFlags.filter { parsedOptions.hasArgument($0) }
3322+
if parsedOptions.hasArgument(.csProfileGenerate),
3323+
parsedOptions.hasArgument(.csProfileGenerateEq)
3324+
{
3325+
// If both forms were specified, report a clear conflict.
3326+
diagnosticEngine.emit(
3327+
.error(Error.conflictingOptions(.csProfileGenerate, .csProfileGenerateEq)),
3328+
location: nil
3329+
)
3330+
providedGen.removeAll { $0 == .csProfileGenerateEq }
3331+
}
3332+
3333+
guard providedGen.count >= 2 else { return }
3334+
for i in 1..<providedGen.count {
3335+
let error = Error.conflictingOptions(providedGen[i - 1], providedGen[i])
3336+
diagnosticEngine.emit(.error(error), location: nil)
3337+
}
3338+
}
3339+
3340+
static private func validateProfilingUseArgs(
3341+
_ parsedOptions: inout ParsedOptions,
3342+
diagnosticEngine: DiagnosticsEngine
3343+
) {
3344+
let conflictingGenFlags: [Option] = [
3345+
.profileGenerate,
3346+
.irProfileGenerate,
3347+
]
3348+
let useProfArgs: [Option] = [
3349+
.profileUse,
3350+
.profileSampleUse,
3351+
]
3352+
let providedUse = useProfArgs.filter { parsedOptions.hasArgument($0) }
3353+
guard !providedUse.isEmpty else { return }
3354+
3355+
// At most one *use* option allowed
3356+
if providedUse.count > 1 {
3357+
for i in 0..<(providedUse.count - 1) {
3358+
for j in (i + 1)..<providedUse.count {
3359+
diagnosticEngine.emit(
3360+
.error(Error.conflictingOptions(providedUse[i], providedUse[j])),
3361+
location: nil
3362+
)
3363+
}
3364+
}
3365+
}
3366+
3367+
// If no generate flags, we're good.
3368+
let providedGen = conflictingGenFlags.filter { parsedOptions.hasArgument($0) }
3369+
guard !providedGen.isEmpty else { return }
3370+
3371+
// We already diagnosed if the user passed more than one "use" option
3372+
// (e.g. both `-profile-use` and `-profile-sample-use`). To avoid
3373+
// spamming diagnostics, we now treat the first provided "use" flag
3374+
// as the canonical representative.
3375+
let canonicalUse = providedUse[0]
3376+
3377+
// Generate vs Use are mutually exclusive
3378+
for g in providedGen {
3379+
diagnosticEngine.emit(.error(Error.conflictingOptions(g, canonicalUse)), location: nil)
3380+
}
3381+
}
3382+
3383+
33103384
static func validateProfilingArgs(_ parsedOptions: inout ParsedOptions,
33113385
fileSystem: FileSystem,
33123386
workingDirectory: AbsolutePath?,
33133387
diagnosticEngine: DiagnosticsEngine) {
3314-
let conflictingProfArgs: [Option] = [.profileGenerate,
3315-
.profileUse,
3316-
.profileSampleUse]
3317-
33183388
// Find out which of the mutually exclusive profiling arguments were provided.
3319-
let provided = conflictingProfArgs.filter { parsedOptions.hasArgument($0) }
3320-
3321-
// If there's at least two of them, there's a conflict.
3322-
if provided.count >= 2 {
3323-
for i in 1..<provided.count {
3324-
let error = Error.conflictingOptions(provided[i-1], provided[i])
3325-
diagnosticEngine.emit(.error(error), location: nil)
3326-
}
3327-
}
3389+
validateProfilingGenerateArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
3390+
validateProfilingUseArgs(&parsedOptions, diagnosticEngine: diagnosticEngine)
33283391

33293392
// Ensure files exist for the given paths.
33303393
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
@@ -237,6 +237,9 @@ extension Driver {
237237
try commandLine.appendLast(.RpassMissedEQ, from: &parsedOptions)
238238
try commandLine.appendLast(.suppressWarnings, from: &parsedOptions)
239239
try commandLine.appendLast(.profileGenerate, from: &parsedOptions)
240+
try commandLine.appendLast(.irProfileGenerate, from: &parsedOptions)
241+
try commandLine.appendLast(.csProfileGenerate, from: &parsedOptions)
242+
try commandLine.appendLast(.csProfileGenerateEq, from: &parsedOptions)
240243
try commandLine.appendLast(.profileUse, from: &parsedOptions)
241244
try commandLine.appendLast(.profileCoverageMapping, from: &parsedOptions)
242245
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
@@ -776,6 +776,9 @@ extension Option {
776776
public static let printZeroStats: Option = Option("-print-zero-stats", .flag, attributes: [.helpHidden, .frontend], helpText: "Prints all stats even if they are zero")
777777
public static let profileCoverageMapping: Option = Option("-profile-coverage-mapping", .flag, attributes: [.frontend, .noInteractive], helpText: "Generate coverage data for use with profiled execution counts")
778778
public static let profileGenerate: Option = Option("-profile-generate", .flag, attributes: [.frontend, .noInteractive], helpText: "Generate instrumented code to collect execution counts")
779+
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)")
780+
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)")
781+
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)")
779782
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")
780783
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")
781784
public static let profileStatsEvents: Option = Option("-profile-stats-events", .flag, attributes: [.helpHidden, .frontend], helpText: "Profile changes to stats in -stats-output-dir")
@@ -1749,6 +1752,9 @@ extension Option {
17491752
Option.printZeroStats,
17501753
Option.profileCoverageMapping,
17511754
Option.profileGenerate,
1755+
Option.irProfileGenerate,
1756+
Option.csProfileGenerate,
1757+
Option.csProfileGenerateEq,
17521758
Option.profileSampleUse,
17531759
Option.profileStatsEntities,
17541760
Option.profileStatsEvents,

0 commit comments

Comments
 (0)