diff --git a/src/compiler/clang.rs b/src/compiler/clang.rs index 39c6af1f1..faa2ab7d7 100644 --- a/src/compiler/clang.rs +++ b/src/compiler/clang.rs @@ -118,10 +118,14 @@ counted_array!(pub static ARGS: [ArgInfo; _] = [ take_arg!("-fdebug-compilation-dir", OsString, Separated, PassThrough), flag!("-fmodules", TooHardFlag), flag!("-fno-color-diagnostics", NoDiagnosticsColorFlag), + flag!("-fno-profile-instr-generate", TooHardFlag), + flag!("-fno-profile-instr-use", TooHardFlag), take_arg!("-fplugin", PathBuf, CanBeConcatenated('='), ExtraHashFile), flag!("-fprofile-instr-generate", ProfileGenerate), - // Can be either -fprofile-instr-use or -fprofile-instr-use=path - take_arg!("-fprofile-instr-use", OsString, Concatenated, TooHard), + // Note: the PathBuf argument is optional + take_arg!("-fprofile-instr-use", PathBuf, Concatenated('='), ClangProfileUse), + // Note: this overrides the -fprofile-use option in gcc.rs. + take_arg!("-fprofile-use", PathBuf, Concatenated('='), ClangProfileUse), take_arg!("-fsanitize-blacklist", PathBuf, Concatenated('='), ExtraHashFile), take_arg!("-gcc-toolchain", OsString, Separated, PassThrough), take_arg!("-include-pch", PathBuf, CanBeSeparated, PreprocessorArgumentPath), @@ -132,6 +136,27 @@ counted_array!(pub static ARGS: [ArgInfo; _] = [ flag!("-verify", PreprocessorArgumentFlag), ]); +// Maps the `-fprofile-use` argument to the actual path of the +// .profdata file Clang will try to use. +pub(crate) fn resolve_profile_use_path(arg: &Path, cwd: &Path) -> PathBuf { + // Note that `arg` might be empty (if no argument was given to + // -fprofile-use), in which case `path` will be `cwd` after + // the next statement and "./default.profdata" at the end of the + // block. This matches Clang's behavior for when no argument is + // given. + let mut path = cwd.join(arg); + + assert!(!arg.as_os_str().is_empty() || path == cwd); + + // Clang allows specifying a directory here, in which case it + // will look for the file `default.profdata` in that directory. + if path.is_dir() { + path.push("default.profdata"); + } + + path +} + #[cfg(test)] mod test { use super::*; @@ -406,4 +431,104 @@ mod test { let a = parses!("-c", "foo.c", "-o", "foo.o"); assert_eq!(a.color_mode, ColorMode::Auto); } + + #[test] + fn test_parse_arguments_profile_instr_use() { + let a = parses!( + "-c", + "foo.c", + "-o", + "foo.o", + "-fprofile-instr-use=foo.profdata" + ); + assert_eq!(ovec!["-fprofile-instr-use=foo.profdata"], a.common_args); + assert_eq!( + ovec![std::env::current_dir().unwrap().join("foo.profdata")], + a.extra_hash_files + ); + } + + #[test] + fn test_parse_arguments_profile_use() { + let a = parses!("-c", "foo.c", "-o", "foo.o", "-fprofile-use=xyz.profdata"); + + assert_eq!(ovec!["-fprofile-use=xyz.profdata"], a.common_args); + assert_eq!( + ovec![std::env::current_dir().unwrap().join("xyz.profdata")], + a.extra_hash_files + ); + } + + #[test] + fn test_parse_arguments_profile_use_with_directory() { + let a = parses!("-c", "foo.c", "-o", "foo.o", "-fprofile-use=."); + + assert_eq!(ovec!["-fprofile-use=."], a.common_args); + assert_eq!( + ovec![std::env::current_dir().unwrap().join("default.profdata")], + a.extra_hash_files + ); + } + + #[test] + fn test_parse_arguments_profile_use_with_no_argument() { + let a = parses!("-c", "foo.c", "-o", "foo.o", "-fprofile-use"); + + assert_eq!(ovec!["-fprofile-use"], a.common_args); + assert_eq!( + ovec![std::env::current_dir().unwrap().join("default.profdata")], + a.extra_hash_files + ); + } + + #[test] + fn test_parse_arguments_pgo_cancellation() { + assert_eq!( + CompilerArguments::CannotCache("-fno-profile-use", None), + parse_arguments_(stringvec![ + "-c", + "foo.c", + "-o", + "foo.o", + "-fprofile-use", + "-fno-profile-use" + ]) + ); + + assert_eq!( + CompilerArguments::CannotCache("-fno-profile-instr-use", None), + parse_arguments_(stringvec![ + "-c", + "foo.c", + "-o", + "foo.o", + "-fprofile-instr-use", + "-fno-profile-instr-use" + ]) + ); + + assert_eq!( + CompilerArguments::CannotCache("-fno-profile-generate", None), + parse_arguments_(stringvec![ + "-c", + "foo.c", + "-o", + "foo.o", + "-fprofile-generate", + "-fno-profile-generate" + ]) + ); + + assert_eq!( + CompilerArguments::CannotCache("-fno-profile-instr-generate", None), + parse_arguments_(stringvec![ + "-c", + "foo.c", + "-o", + "foo.o", + "-fprofile-instr-generate", + "-fno-profile-instr-generate" + ]) + ); + } } diff --git a/src/compiler/gcc.rs b/src/compiler/gcc.rs index 4ce8491f0..4c2e5ef9e 100644 --- a/src/compiler/gcc.rs +++ b/src/compiler/gcc.rs @@ -122,6 +122,7 @@ ArgData! { pub Language(OsString), SplitDwarf, ProfileGenerate, + ClangProfileUse(PathBuf), TestCoverage, Coverage, ExtraHashFile(PathBuf), @@ -168,6 +169,8 @@ counted_array!(pub static ARGS: [ArgInfo; _] = [ flag!("-c", DoCompilation), take_arg!("-fdiagnostics-color", OsString, Concatenated('='), DiagnosticsColor), flag!("-fno-diagnostics-color", NoDiagnosticsColorFlag), + flag!("-fno-profile-generate", TooHardFlag), + flag!("-fno-profile-use", TooHardFlag), flag!("-fno-working-directory", PreprocessorArgumentFlag), flag!("-fplugin=libcc1plugin", TooHardFlag), flag!("-fprofile-arcs", ProfileGenerate), @@ -278,6 +281,9 @@ where OsString::from(arg.flag_str().expect("Compilation flag expected")); } Some(ProfileGenerate) => profile_generate = true, + Some(ClangProfileUse(path)) => { + extra_hash_files.push(clang::resolve_profile_use_path(path, cwd)); + } Some(TestCoverage) => outputs_gcno = true, Some(Coverage) => { outputs_gcno = true; @@ -337,6 +343,7 @@ where let args = match arg.get_data() { Some(SplitDwarf) | Some(ProfileGenerate) + | Some(ClangProfileUse(_)) | Some(TestCoverage) | Some(Coverage) | Some(DiagnosticsColor(_)) @@ -380,6 +387,7 @@ where let args = match arg.get_data() { Some(SplitDwarf) | Some(ProfileGenerate) + | Some(ClangProfileUse(_)) | Some(TestCoverage) | Some(Coverage) | Some(DoCompilation) diff --git a/src/compiler/msvc.rs b/src/compiler/msvc.rs index fdce940f8..f863ac211 100644 --- a/src/compiler/msvc.rs +++ b/src/compiler/msvc.rs @@ -603,6 +603,12 @@ pub fn parse_arguments( profile_generate = true; &mut common_args } + + Some(ClangProfileUse(path)) => { + extra_hash_files.push(clang::resolve_profile_use_path(path, cwd)); + &mut common_args + } + Some(ExtraHashFile(path)) => { extra_hash_files.push(cwd.join(path)); &mut common_args @@ -1061,6 +1067,7 @@ mod test { "-Xclang", "host_dictionary.obj", "-clang:-fprofile-generate", + "-clang:-fprofile-use=xyz.profdata", "dictionary.c" ]; let ParsedArguments { @@ -1068,6 +1075,7 @@ mod test { preprocessor_args, common_args, profile_generate, + extra_hash_files, .. } = match parse_arguments(args) { CompilerArguments::Ok(args) => args, @@ -1090,7 +1098,17 @@ mod test { "host_dictionary.obj" ) ); - assert_eq!(common_args, ovec!("-clang:-fprofile-generate")); + assert_eq!( + common_args, + ovec!( + "-clang:-fprofile-generate", + "-clang:-fprofile-use=xyz.profdata" + ) + ); + assert_eq!( + extra_hash_files, + ovec!(std::env::current_dir().unwrap().join("xyz.profdata")) + ); } #[test]