Index: lib/Transforms/Instrumentation/GCOVProfiling.cpp =================================================================== --- lib/Transforms/Instrumentation/GCOVProfiling.cpp +++ lib/Transforms/Instrumentation/GCOVProfiling.cpp @@ -118,7 +118,8 @@ Function *insertFlush(ArrayRef>); void insertIndirectCounterIncrement(); - std::string mangleName(const DICompileUnit *CU, const char *NewStem); + enum GCovFileType { GCNO, GCDA }; + std::string mangleName(const DICompileUnit *CU, GCovFileType NewStem); GCOVOptions Options; @@ -418,24 +419,37 @@ } std::string GCOVProfiler::mangleName(const DICompileUnit *CU, - const char *NewStem) { + GCovFileType NewStem) { if (NamedMDNode *GCov = M->getNamedMetadata("llvm.gcov")) { for (int i = 0, e = GCov->getNumOperands(); i != e; ++i) { MDNode *N = GCov->getOperand(i); + if (N->getNumOperands() == 3) { + // These nodes have no mangling to apply, it's stored mangled in the + // bitcode. + MDString *NotesFile = dyn_cast(N->getOperand(0)); + MDString *DataFile = dyn_cast(N->getOperand(1)); + MDNode *CompileUnit = dyn_cast(N->getOperand(2)); + if (!NotesFile || !DataFile || !CompileUnit) continue; + if (CompileUnit == CU) + return NewStem == GCNO ? NotesFile->getString() + : DataFile->getString(); + } if (N->getNumOperands() != 2) continue; MDString *GCovFile = dyn_cast(N->getOperand(0)); MDNode *CompileUnit = dyn_cast(N->getOperand(1)); if (!GCovFile || !CompileUnit) continue; if (CompileUnit == CU) { - SmallString<128> Filename = GCovFile->getString(); - sys::path::replace_extension(Filename, NewStem); + SmallString<128> Filename; + Filename = GCovFile->getString(); + sys::path::replace_extension(Filename, + NewStem == GCNO ? "gcno" : "gcda"); return Filename.str(); } } } SmallString<128> Filename = CU->getFilename(); - sys::path::replace_extension(Filename, NewStem); + sys::path::replace_extension(Filename, NewStem == GCNO ? "gcno" : "gcda"); StringRef FName = sys::path::filename(Filename); SmallString<128> CurPath; if (sys::fs::current_path(CurPath)) return FName; @@ -501,7 +515,7 @@ continue; std::error_code EC; - raw_fd_ostream out(mangleName(CU, "gcno"), EC, sys::fs::F_None); + raw_fd_ostream out(mangleName(CU, GCNO), EC, sys::fs::F_None); std::string EdgeDestinations; unsigned FunctionIdent = 0; @@ -849,7 +863,7 @@ if (CU->getDWOId()) continue; - std::string FilenameGcda = mangleName(CU, "gcda"); + std::string FilenameGcda = mangleName(CU, GCDA); uint32_t CfgChecksum = FileChecksums.empty() ? 0 : FileChecksums[i]; Builder.CreateCall(StartFile, {Builder.CreateGlobalStringPtr(FilenameGcda), Index: test/Transforms/GCOVProfiling/three-element-mdnode.ll =================================================================== --- test/Transforms/GCOVProfiling/three-element-mdnode.ll +++ test/Transforms/GCOVProfiling/three-element-mdnode.ll @@ -0,0 +1,27 @@ +; RUN: echo '!9 = !{!"%T/aaa.gcno", !"%T/bbb.gcda", !0}' > %t1 +; RUN: cat %s %t1 > %t2 +; RUN: opt -insert-gcov-profiling -S -o %t3 < %t2 +; RUN: grep _Z3foov %T/aaa.gcno +; RUN: grep bbb.gcda %t3 +; RUN: rm %T/aaa.gcno + +define void @_Z3foov() !dbg !5 { +entry: + ret void, !dbg !8 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!10} +!llvm.gcov = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.3 (trunk 177323)", isOptimized: false, emissionKind: FullDebug, file: !2, enums: !3, retainedTypes: !3, globals: !3, imports: !3) +!1 = !DIFile(filename: "hello.cc", directory: "/home/nlewycky") +!2 = !DIFile(filename: "hello.cc", directory: "/home/nlewycky") +!3 = !{} +!5 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", line: 1, isLocal: false, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !0, scopeLine: 1, file: !1, scope: !1, type: !6, variables: !3) +!6 = !DISubroutineType(types: !7) +!7 = !{null} +!8 = !DILocation(line: 1, scope: !5) + + +!10 = !{i32 1, !"Debug Info Version", i32 3} Index: tools/clang/include/clang/Driver/CC1Options.td =================================================================== --- tools/clang/include/clang/Driver/CC1Options.td +++ tools/clang/include/clang/Driver/CC1Options.td @@ -192,9 +192,14 @@ HelpText<"Emit a gcov coverage notes file when compiling.">; def femit_coverage_data: Flag<["-"], "femit-coverage-data">, HelpText<"Instrument the program to emit gcov coverage data when run.">; -def coverage_file : Separate<["-"], "coverage-file">, - HelpText<"Emit coverage data to this filename. The extension will be replaced.">; -def coverage_file_EQ : Joined<["-"], "coverage-file=">, Alias; +def coverage_data_file : Separate<["-"], "coverage-data-file">, + HelpText<"Emit coverage data to this filename.">; +def coverage_data_file_EQ : Joined<["-"], "coverage-data-file=">, + Alias; +def coverage_notes_file : Separate<["-"], "coverage-notes-file">, + HelpText<"Emit coverage notes to this filename.">; +def coverage_notes_file_EQ : Joined<["-"], "coverage-notes-file=">, + Alias; def coverage_cfg_checksum : Flag<["-"], "coverage-cfg-checksum">, HelpText<"Emit CFG checksum for functions in .gcno files.">; def coverage_no_function_names_in_data : Flag<["-"], "coverage-no-function-names-in-data">, Index: tools/clang/include/clang/Driver/Options.td =================================================================== --- tools/clang/include/clang/Driver/Options.td +++ tools/clang/include/clang/Driver/Options.td @@ -2102,7 +2102,7 @@ defm : BooleanFFlag<"keep-inline-functions">, Group; -def fprofile_dir : Joined<["-"], "fprofile-dir=">, Group; +def fprofile_dir : Joined<["-"], "fprofile-dir=">, Group; def fuse_ld_EQ : Joined<["-"], "fuse-ld=">, Group; Index: tools/clang/include/clang/Frontend/CodeGenOptions.h =================================================================== --- tools/clang/include/clang/Frontend/CodeGenOptions.h +++ tools/clang/include/clang/Frontend/CodeGenOptions.h @@ -99,9 +99,12 @@ /// The code model to use (-mcmodel). std::string CodeModel; - /// The filename with path we use for coverage files. The extension will be - /// replaced. - std::string CoverageFile; + /// The filename with path we use for coverage data files. The runtime allows further + /// manipulation with the GCOV_PREFIX and GCOV_PREFIX_STRIP environment variables. + std::string CoverageDataFile; + + /// The filename with path we use for coverage notes files. + std::string CoverageNotesFile; /// The version string to put into coverage files. char CoverageVersion[4]; Index: tools/clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- tools/clang/lib/CodeGen/CodeGenModule.cpp +++ tools/clang/lib/CodeGen/CodeGenModule.cpp @@ -4208,18 +4208,24 @@ } void CodeGenModule::EmitCoverageFile() { - if (!getCodeGenOpts().CoverageFile.empty()) { - if (llvm::NamedMDNode *CUNode = TheModule.getNamedMetadata("llvm.dbg.cu")) { - llvm::NamedMDNode *GCov = TheModule.getOrInsertNamedMetadata("llvm.gcov"); - llvm::LLVMContext &Ctx = TheModule.getContext(); - llvm::MDString *CoverageFile = - llvm::MDString::get(Ctx, getCodeGenOpts().CoverageFile); - for (int i = 0, e = CUNode->getNumOperands(); i != e; ++i) { - llvm::MDNode *CU = CUNode->getOperand(i); - llvm::Metadata *Elts[] = {CoverageFile, CU}; - GCov->addOperand(llvm::MDNode::get(Ctx, Elts)); - } - } + if (getCodeGenOpts().CoverageDataFile.empty() && + getCodeGenOpts().CoverageNotesFile.empty()) + return; + + llvm::NamedMDNode *CUNode = TheModule.getNamedMetadata("llvm.dbg.cu"); + if (!CUNode) + return; + + llvm::NamedMDNode *GCov = TheModule.getOrInsertNamedMetadata("llvm.gcov"); + llvm::LLVMContext &Ctx = TheModule.getContext(); + llvm::MDString *CoverageDataFile = + llvm::MDString::get(Ctx, getCodeGenOpts().CoverageDataFile); + llvm::MDString *CoverageNotesFile = + llvm::MDString::get(Ctx, getCodeGenOpts().CoverageNotesFile); + for (int i = 0, e = CUNode->getNumOperands(); i != e; ++i) { + llvm::MDNode *CU = CUNode->getOperand(i); + llvm::Metadata *Elts[] = {CoverageNotesFile, CoverageDataFile, CU}; + GCov->addOperand(llvm::MDNode::get(Ctx, Elts)); } } Index: tools/clang/lib/Driver/Tools.cpp =================================================================== --- tools/clang/lib/Driver/Tools.cpp +++ tools/clang/lib/Driver/Tools.cpp @@ -3612,13 +3612,14 @@ if (C.getArgs().hasArg(options::OPT_c) || C.getArgs().hasArg(options::OPT_S)) { if (Output.isFilename()) { - CmdArgs.push_back("-coverage-file"); - SmallString<128> CoverageFilename; + CmdArgs.push_back("-coverage-notes-file"); + SmallString<128> OutputFilename; if (Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o)) { - CoverageFilename = FinalOutput->getValue(); + OutputFilename = FinalOutput->getValue(); } else { - CoverageFilename = llvm::sys::path::filename(Output.getBaseInput()); + OutputFilename = llvm::sys::path::filename(Output.getBaseInput()); } + SmallString<128> CoverageFilename = OutputFilename; if (llvm::sys::path::is_relative(CoverageFilename)) { SmallString<128> Pwd; if (!llvm::sys::fs::current_path(Pwd)) { @@ -3626,7 +3627,23 @@ CoverageFilename.swap(Pwd); } } + llvm::sys::path::replace_extension(CoverageFilename, "gcno"); CmdArgs.push_back(Args.MakeArgString(CoverageFilename)); + + // Leave -fprofile-dir= an unused argument unless .gcda emission is + // enabled. To be polite, with '-fprofile-arcs -fno-profile-arcs' + // consider the flag used. Because there is no -fno-profile-dir the user + // has no targeted way to suppress the warning. + if (Args.hasArg(options::OPT_fprofile_arcs) || + Args.hasArg(options::OPT_coverage)) { + CmdArgs.push_back("-coverage-data-file"); + if (Arg *FProfileDir = Args.getLastArg(options::OPT_fprofile_dir)) { + CoverageFilename = FProfileDir->getValue(); + llvm::sys::path::append(CoverageFilename, OutputFilename); + } + llvm::sys::path::replace_extension(CoverageFilename, "gcda"); + CmdArgs.push_back(Args.MakeArgString(CoverageFilename)); + } } } } Index: tools/clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- tools/clang/lib/Frontend/CompilerInvocation.cpp +++ tools/clang/lib/Frontend/CompilerInvocation.cpp @@ -637,7 +637,8 @@ Opts.EmitGcovArcs = Args.hasArg(OPT_femit_coverage_data); Opts.EmitGcovNotes = Args.hasArg(OPT_femit_coverage_notes); if (Opts.EmitGcovArcs || Opts.EmitGcovNotes) { - Opts.CoverageFile = Args.getLastArgValue(OPT_coverage_file); + Opts.CoverageDataFile = Args.getLastArgValue(OPT_coverage_data_file); + Opts.CoverageNotesFile = Args.getLastArgValue(OPT_coverage_notes_file); Opts.CoverageExtraChecksum = Args.hasArg(OPT_coverage_cfg_checksum); Opts.CoverageNoFunctionNamesInData = Args.hasArg(OPT_coverage_no_function_names_in_data); Index: tools/clang/test/CodeGen/code-coverage.c =================================================================== --- tools/clang/test/CodeGen/code-coverage.c +++ tools/clang/test/CodeGen/code-coverage.c @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -emit-llvm -disable-red-zone -femit-coverage-data %s -o - | FileCheck %s // RUN: %clang_cc1 -emit-llvm -disable-red-zone -femit-coverage-data -coverage-no-function-names-in-data %s -o - | FileCheck %s --check-prefix WITHOUTNAMES +// RUN: %clang_cc1 -emit-llvm -disable-red-zone -femit-coverage-data -coverage-notes-file=aaa.gcno -coverage-data-file=bbb.gcda %s -o - | FileCheck %s --check-prefix GCOV_FILE_INFO // @@ -28,3 +29,6 @@ // CHECK: void @__llvm_gcov_init() unnamed_addr [[NRZ]] // CHECK: attributes [[NRZ]] = { {{.*}}noredzone{{.*}} } + +// GCOV_FILE_INFO: !llvm.gcov = !{![[GCOV:[0-9]+]]} +// GCOV_FILE_INFO: ![[GCOV]] = !{!"aaa.gcno", !"bbb.gcda", !{{[0-9]+}}} Index: tools/clang/test/Driver/clang_f_opts.c =================================================================== --- tools/clang/test/Driver/clang_f_opts.c +++ tools/clang/test/Driver/clang_f_opts.c @@ -66,6 +66,16 @@ // CHECK-PROFILE-ARCS: "-femit-coverage-data" // CHECK-NO-PROFILE-ARCS-NOT: "-femit-coverage-data" +// RUN: %clang -### -S -fprofile-dir=abc %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-DIR-UNUSED %s +// RUN: %clang -### -S -ftest-coverage -fprofile-dir=abc %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-DIR-UNUSED %s +// RUN: %clang -### -S -fprofile-arcs -fprofile-dir=abc %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-DIR %s +// RUN: %clang -### -S --coverage -fprofile-dir=abc %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-DIR %s +// RUN: %clang -### -S -fprofile-arcs -fno-profile-arcs -fprofile-dir=abc %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-DIR-NEITHER %s +// CHECK-PROFILE-DIR: "-coverage-data-file" "abc +// CHECK-PROFILE-DIR-UNUSED: argument unused +// CHECK-PROFILE-DIR-UNUSED-NOT: "-coverage-data-file" "abc +// CHECK-PROFILE-DIR-NEITHER-NOT: argument unused + // RUN: %clang -### -S -fprofile-generate %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-GENERATE-LLVM %s // RUN: %clang -### -S -fprofile-instr-generate %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-GENERATE %s // RUN: %clang -### -S -fprofile-generate=/some/dir %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-GENERATE-DIR %s @@ -212,7 +222,6 @@ // RUN: -fdefer-pop -fno-defer-pop \ // RUN: -fprefetch-loop-arrays -fno-prefetch-loop-arrays \ // RUN: -fprofile-correction -fno-profile-correction \ -// RUN: -fprofile-dir=bar \ // RUN: -fprofile-values -fno-profile-values \ // RUN: -frounding-math -fno-rounding-math \ // RUN: -fsee -fno-see \ @@ -290,7 +299,6 @@ // RUN: -fkeep-inline-functions \ // RUN: -fno-keep-inline-functions \ // RUN: -freorder-blocks \ -// RUN: -fprofile-dir=/rand/dir \ // RUN: -falign-functions \ // RUN: -falign-functions=1 \ // RUN: -ffloat-store \ @@ -357,7 +365,6 @@ // CHECK-WARNING-DAG: optimization flag '-fkeep-inline-functions' is not supported // CHECK-WARNING-DAG: optimization flag '-fno-keep-inline-functions' is not supported // CHECK-WARNING-DAG: optimization flag '-freorder-blocks' is not supported -// CHECK-WARNING-DAG: optimization flag '-fprofile-dir=/rand/dir' is not supported // CHECK-WARNING-DAG: optimization flag '-falign-functions' is not supported // CHECK-WARNING-DAG: optimization flag '-falign-functions=1' is not supported // CHECK-WARNING-DAG: optimization flag '-ffloat-store' is not supported Index: tools/clang/test/Driver/coverage_no_integrated_as.c =================================================================== --- tools/clang/test/Driver/coverage_no_integrated_as.c +++ tools/clang/test/Driver/coverage_no_integrated_as.c @@ -17,7 +17,7 @@ // RUN: %clang -### -c -fprofile-arcs -no-integrated-as %s -o foo/bar.o 2>&1 | FileCheck -check-prefix=CHECK-GCNO-LOCATION-REL-PATH %s -// CHECK-GCNO-DEFAULT-LOCATION: "-coverage-file" "{{.*}}{{/|\\\\}}coverage_no_integrated_as.c" -// CHECK-GCNO-DEFAULT-LOCATION-NOT: "-coverage-file" "/tmp/{{.*}}/coverage_no_integrated_as.c" -// CHECK-GCNO-LOCATION: "-coverage-file" "{{.*}}/foo/bar.o" -// CHECK-GCNO-LOCATION-REL-PATH: "-coverage-file" "{{.*}}{{/|\\\\}}foo/bar.o" +// CHECK-GCNO-DEFAULT-LOCATION: "-coverage-notes-file" "{{.*}}{{/|\\\\}}coverage_no_integrated_as.c" +// CHECK-GCNO-DEFAULT-LOCATION-NOT: "-coverage-notes-file" "/tmp/{{.*}}/coverage_no_integrated_as.c" +// CHECK-GCNO-LOCATION: "-coverage-notes-file" "{{.*}}/foo/bar.gcno" +// CHECK-GCNO-LOCATION-REL-PATH: "-coverage-notes-file" "{{.*}}{{/|\\\\}}foo/bar.gcno"