Add GCC-compatible flags -fprofile-generate and -fprofile-use

Justin Bogner mail at justinbogner.com
Wed Jul 8 18:10:08 PDT 2015


Diego Novillo <dnovillo at google.com> writes:
> This update incorporates:
>
> - Documentation fix to clarify that directories are created at runtime.
> - Allow -fprofile-generate and -fprofile-instr-generate to have the
> usual last-wins semantics.
> - Move __llvm_profile_recursive_mkdir into its own file in the runtime library.
>
> OK to commit?

I have a couple more comments about the clang patch below, then it's
good. The compiler-rt patch LGTM.

> Thanks.  Diego.
>
> diff --git a/docs/UsersManual.rst b/docs/UsersManual.rst
> index cd1b2b3..20ee569 100644
> --- a/docs/UsersManual.rst
> +++ b/docs/UsersManual.rst
> @@ -1488,6 +1488,45 @@ instrumentation:
>     profile. As you make changes to your code, clang may no longer be able to
>     use the profile data. It will warn you when this happens.
>  
> +Profile generation and use can also be controlled by the GCC-compatible flags
> +``-fprofile-generate`` and ``-fprofile-use``. Although these flags are
> +semantically equivalent to their GCC counterparts, they *do not* handle
> +GCC-compatible profiles. They are only meant to implement GCC's semantics
> +with respect to profile creation and use.
> +
> +.. option:: -fprofile-generate[=<dirname>]
> +
> +  Without any other arguments, ``-fprofile-generate`` behaves identically to
> +  ``-fprofile-instr-generate``. When given a directory name, it generates the
> +  profile file ``default.profraw`` in the directory named ``dirname``. If
> +  ``dirname`` does not exist, it will be created at runtime. The environment
> +  variable ``LLVM_PROFILE_FILE`` can be used to override the directory and
> +  filename for the profile file at runtime. For example,
> +
> +  .. code-block:: console
> +
> +    $ clang++ -O2 -fprofile-generate=yyy/zzz code.cc -o code
> +
> +  When ``code`` is executed, the profile will be written to the file
> +  ``yyy/zzz/default.profraw``. This can be altered at runtime via the
> +  ``LLVM_PROFILE_FILE`` environment variable:
> +
> +  .. code-block:: console
> +
> +    $ LLVM_PROFILE_FILE=/tmp/myprofile/code.profraw ./code
> +
> +  The above invocation will produce the profile file
> +  ``/tmp/myprofile/code.profraw`` instead of ``yyy/zzz/default.profraw``.
> +  Notice that ``LLVM_PROFILE_FILE`` overrides the directory *and* the file
> +  name for the profile file.
> +
> +.. option:: -fprofile-use[=<pathname>]
> +
> +  Without any other arguments, ``-fprofile-use`` behaves identically to
> +  ``-fprofile-instr-use``. Otherwise, if ``pathname`` is the full path to a
> +  profile file, it reads from that file. If ``pathname`` is a directory name,
> +  it reads from ``pathname/default.profdata``.
> +
>  
>  Controlling Size of Debug Information
>  -------------------------------------
> diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
> index df90371..1202724 100644
> --- a/include/clang/Driver/Options.td
> +++ b/include/clang/Driver/Options.td
> @@ -422,13 +422,24 @@ def fprofile_instr_generate : Flag<["-"], "fprofile-instr-generate">,
>  def fprofile_instr_generate_EQ : Joined<["-"], "fprofile-instr-generate=">,
>      Group<f_Group>, Flags<[CC1Option]>, MetaVarName<"<file>">,
>      HelpText<"Generate instrumented code to collect execution counts into <file> (overridden by LLVM_PROFILE_FILE env var)">;
> -def fprofile_instr_use : Flag<["-"], "fprofile-instr-use">, Group<f_Group>;
> +def fprofile_instr_use : Flag<["-"], "fprofile-instr-use">, Group<f_Group>,
> +    Flags<[DriverOption]>;
>  def fprofile_instr_use_EQ : Joined<["-"], "fprofile-instr-use=">,
>      Group<f_Group>, Flags<[CC1Option]>,
>      HelpText<"Use instrumentation data for profile-guided optimization">;
>  def fcoverage_mapping : Flag<["-"], "fcoverage-mapping">,
>      Group<f_Group>, Flags<[CC1Option]>,
>      HelpText<"Generate coverage mapping to enable code coverage analysis">;
> +def fprofile_generate : Flag<["-"], "fprofile-generate">,
> +    Alias<fprofile_instr_generate>;
> +def fprofile_generate_EQ : Joined<["-"], "fprofile-generate=">,
> +    Group<f_Group>, Flags<[DriverOption]>, MetaVarName<"<directory>">,
> +    HelpText<"Generate instrumented code to collect execution counts into <directory>/default.profraw (overridden by LLVM_PROFILE_FILE env var)">;
> +def fprofile_use : Flag<["-"], "fprofile-use">, Group<f_Group>,
> +    Alias<fprofile_instr_use>;
> +def fprofile_use_EQ : Joined<["-"], "fprofile-use=">,
> +    Group<f_Group>, Flags<[DriverOption]>, MetaVarName<"<pathname>">,
> +    HelpText<"Use instrumentation data for profile-guided optimization. If pathname is a directory, it reads from <pathname>/default.profile. Otherwise, it reads from file <pathname>.">;

This should say "default.profdata" here to match the code.

>  
>  def fblocks : Flag<["-"], "fblocks">, Group<f_Group>, Flags<[CC1Option]>,
>    HelpText<"Enable the 'blocks' language feature">;
> @@ -904,7 +915,6 @@ def fpie : Flag<["-"], "fpie">, Group<f_Group>;
>  def fno_pie : Flag<["-"], "fno-pie">, Group<f_Group>;
>  def fprofile_arcs : Flag<["-"], "fprofile-arcs">, Group<f_Group>;
>  def fno_profile_arcs : Flag<["-"], "fno-profile-arcs">, Group<f_Group>;
> -def fprofile_generate : Flag<["-"], "fprofile-generate">, Group<f_Group>;
>  def framework : Separate<["-"], "framework">, Flags<[LinkerInput]>;
>  def frandom_seed_EQ : Joined<["-"], "frandom-seed=">, Group<clang_ignored_f_Group>;
>  def freg_struct_return : Flag<["-"], "freg-struct-return">, Group<f_Group>, Flags<[CC1Option]>,
> @@ -1794,8 +1804,6 @@ defm : BooleanFFlag<"keep-inline-functions">, Group<clang_ignored_gcc_optimizati
>  
>  def fprofile_dir : Joined<["-"], "fprofile-dir=">, Group<clang_ignored_gcc_optimization_f_Group>;
>  
> -defm profile_use : BooleanFFlag<"profile-use">, Group<clang_ignored_gcc_optimization_f_Group>;
> -def fprofile_use_EQ : Joined<["-"], "fprofile-use=">, Group<clang_ignored_gcc_optimization_f_Group>;
>  def fuse_ld_EQ : Joined<["-"], "fuse-ld=">, Group<f_Group>;
>  
>  defm align_functions : BooleanFFlag<"align-functions">, Group<clang_ignored_gcc_optimization_f_Group>;
> diff --git a/include/clang/Frontend/CodeGenOptions.h b/include/clang/Frontend/CodeGenOptions.h
> index 66597bd..53246bc 100644
> --- a/include/clang/Frontend/CodeGenOptions.h
> +++ b/include/clang/Frontend/CodeGenOptions.h
> @@ -155,6 +155,7 @@ public:
>    std::vector<std::string> DependentLibraries;
>  
>    /// Name of the profile file to use as output for -fprofile-instr-generate
> +  /// and -fprofile-generate.
>    std::string InstrProfileOutput;
>  
>    /// Name of the profile file to use with -fprofile-sample-use.
> diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp
> index 463503b..fd70868 100644
> --- a/lib/Driver/ToolChains.cpp
> +++ b/lib/Driver/ToolChains.cpp
> @@ -303,6 +303,7 @@ void Darwin::addProfileRTLibs(const ArgList &Args,
>    if (!(Args.hasFlag(options::OPT_fprofile_arcs, options::OPT_fno_profile_arcs,
>                       false) ||
>          Args.hasArg(options::OPT_fprofile_generate) ||
> +        Args.hasArg(options::OPT_fprofile_generate_EQ) ||
>          Args.hasArg(options::OPT_fprofile_instr_generate) ||
>          Args.hasArg(options::OPT_fprofile_instr_generate_EQ) ||
>          Args.hasArg(options::OPT_fcreate_profile) ||
> diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
> index 1795ccf..ee70793 100644
> --- a/lib/Driver/Tools.cpp
> +++ b/lib/Driver/Tools.cpp
> @@ -2298,6 +2298,7 @@ static void addProfileRT(const ToolChain &TC, const ArgList &Args,
>    if (!(Args.hasFlag(options::OPT_fprofile_arcs, options::OPT_fno_profile_arcs,
>                       false) ||
>          Args.hasArg(options::OPT_fprofile_generate) ||
> +        Args.hasArg(options::OPT_fprofile_generate_EQ) ||
>          Args.hasArg(options::OPT_fprofile_instr_generate) ||
>          Args.hasArg(options::OPT_fprofile_instr_generate_EQ) ||
>          Args.hasArg(options::OPT_fcreate_profile) ||
> @@ -3533,22 +3534,34 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
>    Args.AddAllArgs(CmdArgs, options::OPT_finstrument_functions);
>  
>    if ((Args.hasArg(options::OPT_fprofile_instr_generate) ||
> -       Args.hasArg(options::OPT_fprofile_instr_generate_EQ)) &&
> +       Args.hasArg(options::OPT_fprofile_instr_generate_EQ) ||
> +       Args.hasArg(options::OPT_fprofile_generate_EQ)) &&
>        (Args.hasArg(options::OPT_fprofile_instr_use) ||
> -       Args.hasArg(options::OPT_fprofile_instr_use_EQ)))
> +       Args.hasArg(options::OPT_fprofile_instr_use_EQ) ||
> +       Args.hasArg(options::OPT_fprofile_use_EQ)))
>      D.Diag(diag::err_drv_argument_not_allowed_with)
>          << "-fprofile-instr-generate"
>          << "-fprofile-instr-use";
>  
>    if (Arg *A = Args.getLastArg(options::OPT_fprofile_instr_generate_EQ))
>      A->render(Args, CmdArgs);
> -  else
> +  else if (Arg *A = Args.getLastArg(options::OPT_fprofile_generate_EQ)) {
> +    SmallString<128> Path(A->getValue());
> +    llvm::sys::path::append(Path, "default.profraw");
> +    CmdArgs.push_back(
> +        Args.MakeArgString(Twine("-fprofile-instr-generate=") + Path));
> +  } else
>      Args.AddAllArgs(CmdArgs, options::OPT_fprofile_instr_generate);

I think we want a single call to getLastArg here, with all four
variants. As is, it isn't last wins - it uses -fprofile-instr-generate=,
and if there isn't one of those then it uses -fprofile-generate=, etc.

Also, how is -fprofile-generate handled right now? It seems to be
missing.

Anyways, something like (untested):

    if (Arg *A = Args.getLastArg(options::OPT_fprofile_instr_generate_EQ,
                                 options::OPT_fprofile_instr_generate,
                                 options::OPT_fprofile_generate_EQ,
                                 options::OPT_fprofile_generate)) {
      if (A->getNumValues()) {
        SmallString<128> Path(A->getValue());
        if (A->getOption().matches(options::OPT_fprofile_generate_EQ))
          llvm::sys::path::append(Path, "default.profraw");
        CmdArgs.push_back(
            Args.MakeArgString(Twine("-fprofile-instr-generate=") + Path));
      } else
        CmdArgs.push_back("-fprofile-instr-generate");

>  
>    if (Arg *A = Args.getLastArg(options::OPT_fprofile_instr_use_EQ))
>      A->render(Args, CmdArgs);
> -  else if (Args.hasArg(options::OPT_fprofile_instr_use))
> -    CmdArgs.push_back("-fprofile-instr-use=pgo-data");
> +  else if (Arg *A = Args.getLastArg(options::OPT_fprofile_use_EQ,
> +                                    options::OPT_fprofile_instr_use)) {
> +    SmallString<128> Path(A->getNumValues() == 0 ? "" : A->getValue());
> +    if (Path.empty() || llvm::sys::fs::is_directory(Path))
> +      llvm::sys::path::append(Path, "default.profdata");
> +    CmdArgs.push_back(Args.MakeArgString(Twine("-fprofile-instr-use=") + Path));
> +  }

Similarly here.

>  
>    if (Args.hasArg(options::OPT_ftest_coverage) ||
>        Args.hasArg(options::OPT_coverage))
> @@ -3560,7 +3573,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
>  
>    if (Args.hasArg(options::OPT_fcoverage_mapping) &&
>        !(Args.hasArg(options::OPT_fprofile_instr_generate) ||
> -        Args.hasArg(options::OPT_fprofile_instr_generate_EQ)))
> +        Args.hasArg(options::OPT_fprofile_generate) ||
> +        Args.hasArg(options::OPT_fprofile_instr_generate_EQ) ||
> +        Args.hasArg(options::OPT_fprofile_generate_EQ)))
>      D.Diag(diag::err_drv_argument_only_allowed_with)
>          << "-fcoverage-mapping"
>          << "-fprofile-instr-generate";
> diff --git a/test/Driver/clang_f_opts.c b/test/Driver/clang_f_opts.c
> index 68890a7..74cdf5b 100644
> --- a/test/Driver/clang_f_opts.c
> +++ b/test/Driver/clang_f_opts.c
> @@ -66,6 +66,40 @@
>  // CHECK-PROFILE-ARCS: "-femit-coverage-data"
>  // CHECK-NO-PROFILE-ARCS-NOT: "-femit-coverage-data"
>  
> +// RUN: %clang -### -S -fprofile-generate %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-GENERATE %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
> +// RUN: %clang -### -S -fprofile-instr-generate=/tmp/somefile.profraw %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-GENERATE-FILE %s

Some variants like the following would show that the last-one-wins
semantics work correctly:

  %clang -### -S -fprofile-instr-generate=/tmp/foo.profraw -fprofile-generate=/tmp/somefile.profraw %s

> +// RUN: %clang -### -S -fprofile-generate -fprofile-use %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-generate -fprofile-use=dir %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-generate -fprofile-instr-use %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-generate -fprofile-instr-use=file %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-instr-generate -fprofile-use %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-instr-generate -fprofile-use=dir %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-instr-generate -fprofile-instr-use %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-instr-generate -fprofile-instr-use=file %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-instr-generate=file -fprofile-use %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-instr-generate=file -fprofile-use=dir %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-instr-generate=file -fprofile-instr-use %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-instr-generate=file -fprofile-instr-use=file %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-generate=dir -fprofile-use %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-generate=dir -fprofile-use=dir %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-generate=dir -fprofile-instr-use %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// RUN: %clang -### -S -fprofile-generate=dir -fprofile-instr-use=file %s 2>&1 | FileCheck -check-prefix=CHECK-NO-MIX-GEN-USE %s
> +// CHECK-PROFILE-GENERATE: "-fprofile-instr-generate"
> +// CHECK-PROFILE-GENERATE-DIR: "-fprofile-instr-generate=/some/dir/default.profraw"
> +// CHECK-PROFILE-GENERATE-FILE: "-fprofile-instr-generate=/tmp/somefile.profraw"
> +// CHECK-NO-MIX-GEN-USE: '-fprofile-instr-generate' not allowed with '-fprofile-instr-use'
> +
> +// RUN: %clang -### -S -fprofile-use %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-USE %s
> +// RUN: %clang -### -S -fprofile-instr-use %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-USE %s
> +// RUN: mkdir -p %t.d/some/dir
> +// RUN: %clang -### -S -fprofile-use=%t.d/some/dir %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-USE-DIR %s
> +// RUN: %clang -### -S -fprofile-instr-use=/tmp/somefile.prof %s 2>&1 | FileCheck -check-prefix=CHECK-PROFILE-USE-FILE %s
> +// CHECK-PROFILE-USE: "-fprofile-instr-use=default.profdata"
> +// CHECK-PROFILE-USE-DIR: "-fprofile-instr-use={{.*}}.d/some/dir/default.profdata"
> +// CHECK-PROFILE-USE-FILE: "-fprofile-instr-use=/tmp/somefile.prof"
> +
>  // RUN: %clang -### -S -fvectorize %s 2>&1 | FileCheck -check-prefix=CHECK-VECTORIZE %s
>  // RUN: %clang -### -S -fno-vectorize -fvectorize %s 2>&1 | FileCheck -check-prefix=CHECK-VECTORIZE %s
>  // RUN: %clang -### -S -fno-vectorize %s 2>&1 | FileCheck -check-prefix=CHECK-NO-VECTORIZE %s
> @@ -162,7 +196,6 @@
>  // RUN:     -fprefetch-loop-arrays -fno-prefetch-loop-arrays                  \
>  // RUN:     -fprofile-correction -fno-profile-correction                      \
>  // RUN:     -fprofile-dir=bar                                                 \
> -// RUN:     -fprofile-use -fprofile-use=zed -fno-profile-use                  \
>  // RUN:     -fprofile-values -fno-profile-values                              \
>  // RUN:     -frounding-math -fno-rounding-math                                \
>  // RUN:     -fsee -fno-see                                                    \
> @@ -242,8 +275,6 @@
>  // RUN: -fno-keep-inline-functions                                            \
>  // RUN: -freorder-blocks                                                      \
>  // RUN: -fprofile-dir=/rand/dir                                               \
> -// RUN: -fprofile-use                                                         \
> -// RUN: -fprofile-use=/rand/dir                                               \
>  // RUN: -falign-functions                                                     \
>  // RUN: -falign-functions=1                                                   \
>  // RUN: -ffloat-store                                                         \
> @@ -312,8 +343,6 @@
>  // 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 '-fprofile-use' is not supported
> -// CHECK-WARNING-DAG: optimization flag '-fprofile-use=/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
> diff --git a/test/Profile/Inputs/gcc-flag-compatibility.proftext b/test/Profile/Inputs/gcc-flag-compatibility.proftext
> new file mode 100644
> index 0000000..99d41bb
> --- /dev/null
> +++ b/test/Profile/Inputs/gcc-flag-compatibility.proftext
> @@ -0,0 +1,5 @@
> +main
> +4
> +2
> +1
> +100
> diff --git a/test/Profile/gcc-flag-compatibility.c b/test/Profile/gcc-flag-compatibility.c
> new file mode 100644
> index 0000000..53a7651
> --- /dev/null
> +++ b/test/Profile/gcc-flag-compatibility.c
> @@ -0,0 +1,48 @@
> +// Tests for -fprofile-generate and -fprofile-use flag compatibility. These two
> +// flags behave similarly to their GCC counterparts:
> +//
> +// -fprofile-generate         Generates the profile file ./default.profraw
> +// -fprofile-generate=<dir>   Generates the profile file <dir>/default.profraw
> +// -fprofile-use              Uses the profile file ./default.profdata
> +// -fprofile-use=<dir>        Uses the profile file <dir>/default.profdata
> +// -fprofile-use=<dir>/file   Uses the profile file <dir>/file
> +
> +// Check that -fprofile-generate uses the runtime default profile file.
> +// RUN: %clang %s -c -S -o - -emit-llvm -fprofile-generate | FileCheck -check-prefix=PROFILE-GEN %s
> +// PROFILE-GEN: @__llvm_profile_runtime = external global i32
> +// PROFILE-GEN-NOT: call void @__llvm_profile_override_default_filename
> +// PROFILE-GEN-NOT: declare void @__llvm_profile_override_default_filename(i8*)
> +
> +// Check that -fprofile-generate=/path/to generates /path/to/default.profraw
> +// RUN: %clang %s -c -S -o - -emit-llvm -fprofile-generate=/path/to | FileCheck -check-prefix=PROFILE-GEN-EQ %s
> +// PROFILE-GEN-EQ: private constant [25 x i8] c"/path/to/default.profraw\00"
> +// PROFILE-GEN-EQ: call void @__llvm_profile_override_default_filename(i8* getelementptr inbounds ([25 x i8], [25 x i8]* @0, i32 0, i32 0))
> +// PROFILE-GEN-EQ: declare void @__llvm_profile_override_default_filename(i8*)
> +
> +// Check that -fprofile-use reads default.profdata
> +// RUN: llvm-profdata merge %S/Inputs/gcc-flag-compatibility.proftext -o default.profdata
> +// RUN: %clang %s -o - -mllvm -disable-llvm-optzns -emit-llvm -S -fprofile-use | FileCheck -check-prefix=PROFILE-USE-1 %s
> +// PROFILE-USE-1: = !{!"branch_weights", i32 101, i32 2}
> +
> +// Check that -fprofile-use=some/path reads some/path/default.profdata
> +// RUN: rm -rf %t.dir
> +// RUN: mkdir -p %t.dir/some/path
> +// RUN: llvm-profdata merge %S/Inputs/gcc-flag-compatibility.proftext -o %t.dir/some/path/default.profdata
> +// RUN: %clang %s -o - -mllvm -disable-llvm-optzns -emit-llvm -S -fprofile-use=%t.dir/some/path | FileCheck -check-prefix=PROFILE-USE-2 %s
> +// PROFILE-USE-2: = !{!"branch_weights", i32 101, i32 2}
> +
> +// Check that -fprofile-use=some/path/file.prof reads some/path/file.prof
> +// RUN: rm -rf %t.dir
> +// RUN: mkdir -p %t.dir/some/path
> +// RUN: llvm-profdata merge %S/Inputs/gcc-flag-compatibility.proftext -o %t.dir/some/path/file.prof
> +// RUN: %clang %s -o - -mllvm -disable-llvm-optzns -emit-llvm -S -fprofile-use=%t.dir/some/path/file.prof | FileCheck -check-prefix=PROFILE-USE-3 %s
> +// PROFILE-USE-3: = !{!"branch_weights", i32 101, i32 2}
> +
> +int X = 0;
> +
> +int main() {
> +  int i;
> +  for (i = 0; i < 100; i++)
> +    X += i;
> +  return 0;
> +}
>
> diff --git a/lib/profile/CMakeLists.txt b/lib/profile/CMakeLists.txt
> index d0714e0..d03409f 100644
> --- a/lib/profile/CMakeLists.txt
> +++ b/lib/profile/CMakeLists.txt
> @@ -7,7 +7,8 @@ set(PROFILE_SOURCES
>    InstrProfilingFile.c
>    InstrProfilingPlatformDarwin.c
>    InstrProfilingPlatformOther.c
> -  InstrProfilingRuntime.cc)
> +  InstrProfilingRuntime.cc
> +  InstrProfilingUtil.c)
>  
>  if(APPLE)
>    add_compiler_rt_osx_static_runtime(clang_rt.profile_osx
> diff --git a/lib/profile/GCDAProfiling.c b/lib/profile/GCDAProfiling.c
> index e32e97f..aec2328 100644
> --- a/lib/profile/GCDAProfiling.c
> +++ b/lib/profile/GCDAProfiling.c
> @@ -20,6 +20,8 @@
>  |*
>  \*===----------------------------------------------------------------------===*/
>  
> +#include "InstrProfilingUtil.h"
> +
>  #include <errno.h>
>  #include <fcntl.h>
>  #include <stdio.h>
> @@ -27,17 +29,9 @@
>  #include <string.h>
>  #include <sys/mman.h>
>  #include <sys/file.h>
> -#ifdef _WIN32
> -#include <direct.h>
> -#endif
>  
>  #define I386_FREEBSD (defined(__FreeBSD__) && defined(__i386__))
>  
> -#if !I386_FREEBSD
> -#include <sys/stat.h>
> -#include <sys/types.h>
> -#endif
> -
>  #if !defined(_MSC_VER) && !I386_FREEBSD
>  #include <stdint.h>
>  #endif
> @@ -52,7 +46,6 @@ typedef unsigned long long uint64_t;
>  typedef unsigned char uint8_t;
>  typedef unsigned int uint32_t;
>  typedef unsigned long long uint64_t;
> -int mkdir(const char*, unsigned short);
>  #endif
>  
>  /* #define DEBUG_GCDAPROFILING */
> @@ -209,21 +202,6 @@ static char *mangle_filename(const char *orig_filename) {
>    return new_filename;
>  }
>  
> -static void recursive_mkdir(char *path) {
> -  int i;
> -
> -  for (i = 1; path[i] != '\0'; ++i) {
> -    if (path[i] != '/') continue;
> -    path[i] = '\0';
> -#ifdef _WIN32
> -    _mkdir(path);
> -#else
> -    mkdir(path, 0755);  /* Some of these will fail, ignore it. */
> -#endif
> -    path[i] = '/';
> -  }
> -}
> -
>  static int map_file() {
>    fseek(output_file, 0L, SEEK_END);
>    file_size = ftell(output_file);
> @@ -283,7 +261,7 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4],
>      fd = open(filename, O_RDWR | O_CREAT, 0644);
>      if (fd == -1) {
>        /* Try creating the directories first then opening the file. */
> -      recursive_mkdir(filename);
> +      __llvm_profile_recursive_mkdir(filename);
>        fd = open(filename, O_RDWR | O_CREAT, 0644);
>        if (fd == -1) {
>          /* Bah! It's hopeless. */
> diff --git a/lib/profile/InstrProfiling.h b/lib/profile/InstrProfiling.h
> index 84b673c..3778a88 100644
> --- a/lib/profile/InstrProfiling.h
> +++ b/lib/profile/InstrProfiling.h
> @@ -64,7 +64,7 @@ uint64_t *__llvm_profile_end_counters(void);
>   * or if it hasn't been called, the \c LLVM_PROFILE_FILE environment variable,
>   * or if that's not set, the last name given to
>   * \a __llvm_profile_override_default_filename(), or if that's not set,
> - * \c "default.profdata".
> + * \c "default.profraw".
>   */
>  int __llvm_profile_write_file(void);
>  
> diff --git a/lib/profile/InstrProfilingFile.c b/lib/profile/InstrProfilingFile.c
> index 0102a25..9402238 100644
> --- a/lib/profile/InstrProfilingFile.c
> +++ b/lib/profile/InstrProfilingFile.c
> @@ -8,6 +8,7 @@
>  \*===----------------------------------------------------------------------===*/
>  
>  #include "InstrProfiling.h"
> +#include "InstrProfilingUtil.h"
>  #include <errno.h>
>  #include <stdio.h>
>  #include <stdlib.h>
> @@ -84,6 +85,13 @@ static void truncateCurrentFile(void) {
>    if (!Filename || !Filename[0])
>      return;
>  
> +  /* Create the directory holding the file, if needed. */
> +  if (strchr(Filename, '/')) {
> +    char *Copy = malloc(strlen(Filename) + 1);
> +    strcpy(Copy, Filename);
> +    __llvm_profile_recursive_mkdir(Copy);
> +  }
> +
>    /* Truncate the file.  Later we'll reopen and append. */
>    File = fopen(Filename, "w");
>    if (!File)
> diff --git a/lib/profile/InstrProfilingUtil.c b/lib/profile/InstrProfilingUtil.c
> new file mode 100644
> index 0000000..e146dfc
> --- /dev/null
> +++ b/lib/profile/InstrProfilingUtil.c
> @@ -0,0 +1,35 @@
> +/*===- InstrProfilingUtil.c - Support library for PGO instrumentation -----===*\
> +|*
> +|*                     The LLVM Compiler Infrastructure
> +|*
> +|* This file is distributed under the University of Illinois Open Source
> +|* License. See LICENSE.TXT for details.
> +|*
> +\*===----------------------------------------------------------------------===*/
> +
> +#include "InstrProfilingUtil.h"
> +
> +#ifdef _WIN32
> +#include <direct.h>
> +#elif I386_FREEBSD
> +int mkdir(const char*, unsigned short);
> +#else
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +#endif
> +
> +__attribute__((visibility("hidden")))
> +void __llvm_profile_recursive_mkdir(char *path) {
> +  int i;
> +
> +  for (i = 1; path[i] != '\0'; ++i) {
> +    if (path[i] != '/') continue;
> +    path[i] = '\0';
> +#ifdef _WIN32
> +    _mkdir(path);
> +#else
> +    mkdir(path, 0755);  /* Some of these will fail, ignore it. */
> +#endif
> +    path[i] = '/';
> +  }
> +}
> diff --git a/lib/profile/InstrProfilingUtil.h b/lib/profile/InstrProfilingUtil.h
> new file mode 100644
> index 0000000..756b18e
> --- /dev/null
> +++ b/lib/profile/InstrProfilingUtil.h
> @@ -0,0 +1,16 @@
> +/*===- InstrProfilingUtil.h - Support library for PGO instrumentation -----===*\
> +|*
> +|*                     The LLVM Compiler Infrastructure
> +|*
> +|* This file is distributed under the University of Illinois Open Source
> +|* License. See LICENSE.TXT for details.
> +|*
> +\*===----------------------------------------------------------------------===*/
> +
> +#ifndef PROFILE_INSTRPROFILINGUTIL_H
> +#define PROFILE_INSTRPROFILINGUTIL_H
> +
> +/*! \brief Create a directory tree. */
> +void __llvm_profile_recursive_mkdir(char *Pathname);
> +
> +#endif  /* PROFILE_INSTRPROFILINGUTIL_H */
> diff --git a/test/profile/Inputs/gcc-flag-compatibility.c b/test/profile/Inputs/gcc-flag-compatibility.c
> new file mode 100644
> index 0000000..1c07bb1
> --- /dev/null
> +++ b/test/profile/Inputs/gcc-flag-compatibility.c
> @@ -0,0 +1,8 @@
> +int X = 0;
> +
> +int main() {
> +  int i;
> +  for (i = 0; i < 100; i++)
> +    X += i;
> +  return 0;
> +}
> diff --git a/test/profile/gcc-flag-compatibility.test b/test/profile/gcc-flag-compatibility.test
> new file mode 100644
> index 0000000..8e8b55d
> --- /dev/null
> +++ b/test/profile/gcc-flag-compatibility.test
> @@ -0,0 +1,17 @@
> +RUN: mkdir -p %t.d
> +RUN: %clang_profgen_gcc=%t.d/d1/d2 -o %t.d/code %S/Inputs/gcc-flag-compatibility.c
> +
> +# Test that the instrumented code writes to %t.d/d1/d2/default.profraw
> +RUN: %run %t.d/code
> +RUN: llvm-profdata merge -o %t.profdata %t.d/d1/d2/default.profraw
> +
> +# Test that we can override the directory and file name with LLVM_PROFILE_FILE.
> +RUN: env LLVM_PROFILE_FILE=%t.d/x1/prof.raw %run %t.d/code
> +RUN: llvm-profdata merge -o %t.profdata %t.d/x1/prof.raw
> +
> +# Test that we can specify a directory with -fprofile-use.
> +RUN: llvm-profdata merge -o %t.d/default.profdata %t.d/x1/prof.raw
> +RUN: %clang_profuse_gcc=%t.d -o %t.d/code %S/Inputs/gcc-flag-compatibility.c
> +
> +# Test that we can specify a file with -fprofile-use.
> +RUN: %clang_profuse_gcc=%t.profdata -o %t.d/code %S/Inputs/gcc-flag-compatibility.c
> diff --git a/test/profile/lit.cfg b/test/profile/lit.cfg
> index e4910ab..b1b44a1 100644
> --- a/test/profile/lit.cfg
> +++ b/test/profile/lit.cfg
> @@ -45,6 +45,8 @@ def build_invocation(compile_flags):
>  config.substitutions.append( ("%clang ", build_invocation(clang_cflags)) )
>  config.substitutions.append( ("%clang_profgen ", build_invocation(clang_cflags) + " -fprofile-instr-generate ") )
>  config.substitutions.append( ("%clang_profuse=", build_invocation(clang_cflags) + " -fprofile-instr-use=") )
> +config.substitutions.append( ("%clang_profgen_gcc=", build_invocation(clang_cflags) + " -fprofile-generate=") )
> +config.substitutions.append( ("%clang_profuse_gcc=", build_invocation(clang_cflags) + " -fprofile-use=") )
>  
>  if config.host_os not in ['Darwin', 'FreeBSD', 'Linux']:
>    config.unsupported = True



More information about the cfe-commits mailing list