r262420 - clang-cl: Implement initial limited support for precompiled headers.

Nico Weber via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 1 15:16:45 PST 2016


Author: nico
Date: Tue Mar  1 17:16:44 2016
New Revision: 262420

URL: http://llvm.org/viewvc/llvm-project?rev=262420&view=rev
Log:
clang-cl: Implement initial limited support for precompiled headers.

In the gcc precompiled header model, one explicitly runs clang with `-x
c++-header` on a .h file to produce a gch file, and then includes the header
with `-include foo.h` and if a .gch file exists for that header it gets used.
This is documented at
http://clang.llvm.org/docs/UsersManual.html#precompiled-headers

cl.exe's model is fairly different, and controlled by the two flags /Yc and
/Yu. A pch file is generated as a side effect of a regular compilation when
/Ycheader.h is passed. While the compilation is running, the compiler keeps
track of #include lines in the main translation unit and writes everything up
to an `#include "header.h"` line into a pch file. Conversely, /Yuheader.h tells
the compiler to skip all code in the main TU up to and including `#include
"header.h"` and instead load header.pch. (It's also possible to use /Yc and /Yu
without an argument, in that case a `#pragma hrdstop` takes the role of
controlling the point where pch ends and real code begins.)

This patch implements limited support for this in that it requires the pch
header to be passed as a /FI force include flag – with this restriction,
it can be implemented almost completely in the driver with fairly small amounts
of code. For /Yu, this is trivial, and for /Yc a separate pch action is added
that runs before the actual compilation. After r261774, the first failing
command makes a compilation stop – this means if the pch fails to build the
main compilation won't run, which is what we want. However, in /fallback builds
we need to run the main compilation even if the pch build fails so that the
main compilation's fallback can run. To achieve this, add a ForceSuccessCommand
that pretends that the pch build always succeeded in /fallback builds (the main
compilation will then fail to open the pch and run the fallback cl.exe
invocation).

If /Yc /Yu are used in a setup that clang-cl doesn't implement yet, clang-cl
will now emit a "not implemented yet; flag ignored" warning that can be
disabled using -Wno-clang-cl-pch.

Since clang-cl doesn't yet serialize some important things (most notably
`pragma comment(lib, ...)`, this feature is disabled by default and only
enabled by an internal driver flag. Once it's more stable, this internal flag
will disappear.

(The default stdafx.h setup passes stdafx.h as explicit argument to /Yc but not
as /FI – instead every single TU has to `#include <stdafx.h>` as first thing it
does. Implementing support for this should be possible with the approach in
this patch with minimal frontend changes by passing a --stop-at / --start-at
flag from the driver to the frontend. This is left for a follow-up. I don't
think we ever want to support `#pragma hdrstop`, and supporting it with this
approach isn't easy: This approach relies on the driver knowing the pch
filename in advance, and `#pragma hdrstop(out.pch)` can set the output
filename, so the driver can't know about it in advance.)

clang-cl now also honors /Fp and puts pch files in the same spot that cl.exe
would put them, but the pch file format is of course incompatible. This has
ramifications on /fallback, so /Yc /Yu aren't passed through to cl.exe in
/fallback builds.

http://reviews.llvm.org/D17695

Added:
    cfe/trunk/test/Driver/Inputs/pchfile.cpp
    cfe/trunk/test/Driver/Inputs/pchfile.h
    cfe/trunk/test/Driver/cl-pch-errorhandling.cpp
    cfe/trunk/test/Driver/cl-pch-search.cpp
    cfe/trunk/test/Driver/cl-pch.c
    cfe/trunk/test/Driver/cl-pch.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticDriverKinds.td
    cfe/trunk/include/clang/Basic/DiagnosticGroups.td
    cfe/trunk/include/clang/Driver/CC1Options.td
    cfe/trunk/include/clang/Driver/CLCompatOptions.td
    cfe/trunk/include/clang/Driver/Driver.h
    cfe/trunk/include/clang/Driver/Job.h
    cfe/trunk/include/clang/Frontend/CompilerInstance.h
    cfe/trunk/include/clang/Frontend/FrontendOptions.h
    cfe/trunk/lib/Driver/Driver.cpp
    cfe/trunk/lib/Driver/Job.cpp
    cfe/trunk/lib/Driver/Tools.cpp
    cfe/trunk/lib/Frontend/CompilerInstance.cpp
    cfe/trunk/lib/Frontend/CompilerInvocation.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticDriverKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticDriverKinds.td?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticDriverKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticDriverKinds.td Tue Mar  1 17:16:44 2016
@@ -98,6 +98,20 @@ def err_drv_unknown_argument : Error<"un
 def warn_drv_unknown_argument_clang_cl : Warning<
   "unknown argument ignored in clang-cl: '%0'">,
   InGroup<UnknownArgument>;
+
+def warn_drv_ycyu_no_arg_clang_cl : Warning<
+  "support for '%0' without a filename not implemented yet; flag ignored">,
+  InGroup<ClangClPch>;
+def warn_drv_ycyu_different_arg_clang_cl : Warning<
+  "support for '/Yc' and '/Yu' with different filenames not implemented yet; flags ignored">,
+  InGroup<ClangClPch>;
+def warn_drv_ycyu_no_fi_arg_clang_cl : Warning<
+  "support for '%0' without a corresponding /FI flag not implemented yet; flag ignored">,
+  InGroup<ClangClPch>;
+def warn_drv_yc_multiple_inputs_clang_cl : Warning<
+  "support for '/Yc' with more than one source file not implemented yet; flag ignored">,
+  InGroup<ClangClPch>;
+
 def err_drv_invalid_value : Error<"invalid value '%1' in '%0'">;
 def err_drv_invalid_int_value : Error<"invalid integral value '%1' in '%0'">;
 def err_drv_invalid_remap_file : Error<

Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Tue Mar  1 17:16:44 2016
@@ -785,6 +785,8 @@ def Microsoft : DiagGroup<"microsoft",
      MicrosoftConstInit, MicrosoftVoidPseudoDtor, MicrosoftAnonTag,
      MicrosoftCommentPaste, MicrosoftEndOfFile]>;
 
+def ClangClPch : DiagGroup<"clang-cl-pch">;
+
 def ObjCNonUnifiedException : DiagGroup<"objc-nonunified-exceptions">;
 
 def ObjCProtocolMethodImpl : DiagGroup<"objc-protocol-method-implementation">;

Modified: cfe/trunk/include/clang/Driver/CC1Options.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/CC1Options.td?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/CC1Options.td (original)
+++ cfe/trunk/include/clang/Driver/CC1Options.td Tue Mar  1 17:16:44 2016
@@ -495,6 +495,10 @@ def fixit_to_temp : Flag<["-"], "fixit-t
 
 def foverride_record_layout_EQ : Joined<["-"], "foverride-record-layout=">,
   HelpText<"Override record layouts with those in the given file">;
+def find_pch_source_EQ : Joined<["-"], "find-pch-source=">,
+  HelpText<"When building a pch, try to find the input file in include "
+           "directories, as if it had been included by the argument passed "
+           "to this flag.">;
   
 //===----------------------------------------------------------------------===//
 // Language Options

Modified: cfe/trunk/include/clang/Driver/CLCompatOptions.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/CLCompatOptions.td?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/CLCompatOptions.td (original)
+++ cfe/trunk/include/clang/Driver/CLCompatOptions.td Tue Mar  1 17:16:44 2016
@@ -20,6 +20,9 @@ def cl_compile_Group : OptionGroup<"<cla
 def cl_ignored_Group : OptionGroup<"<clang-cl ignored options>">,
   Group<cl_Group>;
 
+def cl_internal_Group : OptionGroup<"<clang-cl internal options>">,
+  Group<cl_Group>;
+
 class CLFlag<string name> : Option<["/", "-"], name, KIND_FLAG>,
   Group<cl_Group>, Flags<[CLOption, DriverOption]>;
 
@@ -29,6 +32,9 @@ class CLCompileFlag<string name> : Optio
 class CLIgnoredFlag<string name> : Option<["/", "-"], name, KIND_FLAG>,
   Group<cl_ignored_Group>, Flags<[CLOption, DriverOption, HelpHidden]>;
 
+class CLInternalFlag<string name> : Option<["-"], name, KIND_FLAG>,
+  Group<cl_internal_Group>, Flags<[CLOption, DriverOption, HelpHidden]>;
+
 class CLJoined<string name> : Option<["/", "-"], name, KIND_JOINED>,
   Group<cl_Group>, Flags<[CLOption, DriverOption]>;
 
@@ -252,6 +258,23 @@ def _SLASH_volatile_ms  : Option<["/", "
 def _SLASH_Zl : CLFlag<"Zl">,
   HelpText<"Don't mention any default libraries in the object file">;
 
+def _SLASH_Yc : CLJoined<"Yc">,
+  HelpText<"Generate a pch file for all code up to and including <filename>">,
+  MetaVarName<"<filename>">;
+def _SLASH_Yu : CLJoined<"Yu">,
+  HelpText<"Load a pch file and use it instead of all code up to "
+           "and including <filename>">,
+  MetaVarName<"<filename>">;
+def _SLASH_Y_ : CLFlag<"Y-">,
+  HelpText<"Disable precompiled headers, overrides /Yc and /Yu">;
+def _SLASH_Fp : CLJoined<"Fp">,
+  HelpText<"Set pch filename (with /Yc and /Yu)">, MetaVarName<"<filename>">;
+
+// Internal:
+// FIXME: Once /Yc support is stable enough, turn it on by default (when /Yc
+// is passed) and remove this flag.
+def _SLASH_internal_enable_pch : CLInternalFlag<"internal-enable-pch">;
+
 // Ignored:
 
 def _SLASH_analyze_ : CLIgnoredFlag<"analyze-">;
@@ -294,7 +317,6 @@ def _SLASH_favor : CLJoined<"favor">;
 def _SLASH_FC : CLFlag<"FC">;
 def _SLASH_F : CLFlag<"F">;
 def _SLASH_Fm : CLJoined<"Fm">;
-def _SLASH_Fp : CLJoined<"Fp">;
 def _SLASH_Fr : CLJoined<"Fr">;
 def _SLASH_FR : CLJoined<"FR">;
 def _SLASH_FU : CLJoinedOrSeparate<"FU">;
@@ -332,11 +354,8 @@ def _SLASH_V : CLFlag<"V">;
 def _SLASH_WL : CLFlag<"WL">;
 def _SLASH_Wp64 : CLFlag<"Wp64">;
 def _SLASH_X : CLFlag<"X">;
-def _SLASH_Yc : CLJoined<"Yc">;
-def _SLASH_Y_ : CLFlag<"Y-">;
 def _SLASH_Yd : CLFlag<"Yd">;
 def _SLASH_Yl : CLJoined<"Yl">;
-def _SLASH_Yu : CLJoined<"Yu">;
 def _SLASH_Za : CLFlag<"Za">;
 def _SLASH_Zc : CLJoined<"Zc:">;
 def _SLASH_Ze : CLFlag<"Ze">;

Modified: cfe/trunk/include/clang/Driver/Driver.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/Driver.h?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/Driver.h (original)
+++ cfe/trunk/include/clang/Driver/Driver.h Tue Mar  1 17:16:44 2016
@@ -423,6 +423,9 @@ public:
   /// GCC goes to extra lengths here to be a bit more robust.
   std::string GetTemporaryPath(StringRef Prefix, const char *Suffix) const;
 
+  /// Return the pathname of the pch file in clang-cl mode.
+  std::string GetClPchPath(Compilation &C, StringRef BaseName) const;
+
   /// ShouldUseClangCompiler - Should the clang compiler be used to
   /// handle this action.
   bool ShouldUseClangCompiler(const JobAction &JA) const;

Modified: cfe/trunk/include/clang/Driver/Job.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/Job.h?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/Job.h (original)
+++ cfe/trunk/include/clang/Driver/Job.h Tue Mar  1 17:16:44 2016
@@ -138,6 +138,20 @@ private:
   std::unique_ptr<Command> Fallback;
 };
 
+/// Like Command, but always pretends that the wrapped command succeeded.
+class ForceSuccessCommand : public Command {
+public:
+  ForceSuccessCommand(const Action &Source_, const Tool &Creator_,
+                      const char *Executable_, const ArgStringList &Arguments_,
+                      ArrayRef<InputInfo> Inputs);
+
+  void Print(llvm::raw_ostream &OS, const char *Terminator, bool Quote,
+             CrashReportInfo *CrashInfo = nullptr) const override;
+
+  int Execute(const StringRef **Redirects, std::string *ErrMsg,
+              bool *ExecutionFailed) const override;
+};
+
 /// JobList - A sequence of jobs to perform.
 class JobList {
 public:

Modified: cfe/trunk/include/clang/Frontend/CompilerInstance.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/CompilerInstance.h?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/CompilerInstance.h (original)
+++ cfe/trunk/include/clang/Frontend/CompilerInstance.h Tue Mar  1 17:16:44 2016
@@ -748,10 +748,11 @@ public:
   ///
   /// \return True on success.
   static bool InitializeSourceManager(const FrontendInputFile &Input,
-                DiagnosticsEngine &Diags,
-                FileManager &FileMgr,
-                SourceManager &SourceMgr,
-                const FrontendOptions &Opts);
+                                      DiagnosticsEngine &Diags,
+                                      FileManager &FileMgr,
+                                      SourceManager &SourceMgr,
+                                      HeaderSearch *HS,
+                                      const FrontendOptions &Opts);
 
   /// }
 

Modified: cfe/trunk/include/clang/Frontend/FrontendOptions.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/FrontendOptions.h?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/FrontendOptions.h (original)
+++ cfe/trunk/include/clang/Frontend/FrontendOptions.h Tue Mar  1 17:16:44 2016
@@ -266,6 +266,10 @@ public:
   /// \brief Auxiliary triple for CUDA compilation.
   std::string AuxTriple;
 
+  /// \brief If non-empty, search the pch input file as it was a header
+  // included by this file.
+  std::string FindPchSource;
+
 public:
   FrontendOptions() :
     DisableFree(false), RelocatablePCH(false), ShowHelp(false),

Modified: cfe/trunk/lib/Driver/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/Driver.cpp?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/Driver.cpp (original)
+++ cfe/trunk/lib/Driver/Driver.cpp Tue Mar  1 17:16:44 2016
@@ -1452,6 +1452,66 @@ void Driver::BuildActions(Compilation &C
     }
   }
 
+  // Diagnose unsupported forms of /Yc /Yu. Ignore /Yc/Yu for now if:
+  // * no filename after it
+  // * both /Yc and /Yu passed but with different filenames
+  // * corresponding file not also passed as /FI
+  Arg *YcArg = Args.getLastArg(options::OPT__SLASH_Yc);
+  Arg *YuArg = Args.getLastArg(options::OPT__SLASH_Yu);
+  if (YcArg && YcArg->getValue()[0] == '\0') {
+    Diag(clang::diag::warn_drv_ycyu_no_arg_clang_cl) << YcArg->getSpelling();
+    Args.eraseArg(options::OPT__SLASH_Yc);
+    YcArg = nullptr;
+  }
+  if (YuArg && YuArg->getValue()[0] == '\0') {
+    Diag(clang::diag::warn_drv_ycyu_no_arg_clang_cl) << YuArg->getSpelling();
+    Args.eraseArg(options::OPT__SLASH_Yu);
+    YuArg = nullptr;
+  }
+  if (YcArg && YuArg && strcmp(YcArg->getValue(), YuArg->getValue()) != 0) {
+    Diag(clang::diag::warn_drv_ycyu_different_arg_clang_cl);
+    Args.eraseArg(options::OPT__SLASH_Yc);
+    Args.eraseArg(options::OPT__SLASH_Yu);
+    YcArg = YuArg = nullptr;
+  }
+  if (YcArg || YuArg) {
+    StringRef Val = YcArg ? YcArg->getValue() : YuArg->getValue();
+    bool FoundMatchingInclude = false;
+    for (const Arg *Inc : Args.filtered(options::OPT_include)) {
+      // FIXME: Do case-insensitive matching and consider / and \ as equal.
+      if (Inc->getValue() == Val)
+        FoundMatchingInclude = true;
+    }
+    if (!FoundMatchingInclude) {
+      Diag(clang::diag::warn_drv_ycyu_no_fi_arg_clang_cl)
+          << (YcArg ? YcArg : YuArg)->getSpelling();
+      Args.eraseArg(options::OPT__SLASH_Yc);
+      Args.eraseArg(options::OPT__SLASH_Yu);
+      YcArg = YuArg = nullptr;
+    }
+  }
+  if (YcArg && Inputs.size() > 1) {
+    Diag(clang::diag::warn_drv_yc_multiple_inputs_clang_cl);
+    Args.eraseArg(options::OPT__SLASH_Yc);
+    YcArg = nullptr;
+  }
+  if (Args.hasArg(options::OPT__SLASH_Y_)) {
+    // /Y- disables all pch handling.  Rather than check for it everywhere,
+    // just remove clang-cl pch-related flags here.
+    Args.eraseArg(options::OPT__SLASH_Fp);
+    Args.eraseArg(options::OPT__SLASH_Yc);
+    Args.eraseArg(options::OPT__SLASH_Yu);
+    YcArg = YuArg = nullptr;
+  }
+  // FIXME: For now, only enable pch support if an internal flag is passed too.
+  // Remove this once pch support has stabilitzed.
+  if (!Args.hasArg(options::OPT__SLASH_internal_enable_pch)) {
+    Args.eraseArg(options::OPT__SLASH_Fp);
+    Args.eraseArg(options::OPT__SLASH_Yc);
+    Args.eraseArg(options::OPT__SLASH_Yu);
+    YcArg = YuArg = nullptr;
+  }
+
   // Construct the actions to perform.
   ActionList LinkerInputs;
 
@@ -1463,6 +1523,25 @@ void Driver::BuildActions(Compilation &C
     PL.clear();
     types::getCompilationPhases(InputType, PL);
 
+    if (YcArg) {
+      // Add a separate precompile phase for the compile phase.
+      if (FinalPhase >= phases::Compile) {
+        llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases> PCHPL;
+        types::getCompilationPhases(types::TY_CXXHeader, PCHPL);
+        Arg *PchInputArg = MakeInputArg(Args, Opts, YcArg->getValue());
+
+        // Build the pipeline for the pch file.
+        Action *ClangClPch = C.MakeAction<InputAction>(*PchInputArg, InputType);
+        for (phases::ID Phase : PCHPL)
+          ClangClPch = ConstructPhaseAction(C, Args, Phase, ClangClPch);
+        assert(ClangClPch);
+        Actions.push_back(ClangClPch);
+        // The driver currently exits after the first failed command.  This
+        // relies on that behavior, to make sure if the pch generation fails,
+        // the main compilation won't run.
+      }
+    }
+
     // If the first step comes after the final phase we are doing as part of
     // this compilation, warn the user about it.
     phases::ID InitialPhase = PL[0];
@@ -2109,8 +2188,11 @@ const char *Driver::GetNamedOutputPath(C
       Output += "-";
       Output.append(BoundArch);
       NamedOutput = C.getArgs().MakeArgString(Output.c_str());
-    } else
+    } else {
       NamedOutput = getDefaultImageName();
+    }
+  } else if (JA.getType() == types::TY_PCH && IsCLMode()) {
+    NamedOutput = C.getArgs().MakeArgString(GetClPchPath(C, BaseName).c_str());
   } else {
     const char *Suffix = types::getTypeTempSuffix(JA.getType(), IsCLMode());
     assert(Suffix && "All types used for output should have a suffix.");
@@ -2276,6 +2358,25 @@ std::string Driver::GetTemporaryPath(Str
   return Path.str();
 }
 
+std::string Driver::GetClPchPath(Compilation &C, StringRef BaseName) const {
+  SmallString<128> Output;
+  if (Arg *FpArg = C.getArgs().getLastArg(options::OPT__SLASH_Fp)) {
+    // FIXME: If anybody needs it, implement this obscure rule:
+    // "If you specify a directory without a file name, the default file name
+    // is VCx0.pch., where x is the major version of Visual C++ in use."
+    Output = FpArg->getValue();
+
+    // "If you do not specify an extension as part of the path name, an
+    // extension of .pch is assumed. "
+    if (!llvm::sys::path::has_extension(Output))
+      Output += ".pch";
+  } else {
+    Output = BaseName;
+    llvm::sys::path::replace_extension(Output, ".pch");
+  }
+  return Output.str();
+}
+
 const ToolChain &Driver::getToolChain(const ArgList &Args,
                                       const llvm::Triple &Target) const {
 

Modified: cfe/trunk/lib/Driver/Job.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/Job.cpp?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/Job.cpp (original)
+++ cfe/trunk/lib/Driver/Job.cpp Tue Mar  1 17:16:44 2016
@@ -297,6 +297,29 @@ int FallbackCommand::Execute(const Strin
   return SecondaryStatus;
 }
 
+ForceSuccessCommand::ForceSuccessCommand(const Action &Source_,
+                                         const Tool &Creator_,
+                                         const char *Executable_,
+                                         const ArgStringList &Arguments_,
+                                         ArrayRef<InputInfo> Inputs)
+    : Command(Source_, Creator_, Executable_, Arguments_, Inputs) {}
+
+void ForceSuccessCommand::Print(raw_ostream &OS, const char *Terminator,
+                            bool Quote, CrashReportInfo *CrashInfo) const {
+  Command::Print(OS, "", Quote, CrashInfo);
+  OS << " || (exit 0)" << Terminator;
+}
+
+int ForceSuccessCommand::Execute(const StringRef **Redirects,
+                                 std::string *ErrMsg,
+                                 bool *ExecutionFailed) const {
+  int Status = Command::Execute(Redirects, ErrMsg, ExecutionFailed);
+  (void)Status;
+  if (ExecutionFailed)
+    *ExecutionFailed = false;
+  return 0;
+}
+
 void JobList::Print(raw_ostream &OS, const char *Terminator, bool Quote,
                     CrashReportInfo *CrashInfo) const {
   for (const auto &Job : *this)

Modified: cfe/trunk/lib/Driver/Tools.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/Tools.cpp?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/Tools.cpp (original)
+++ cfe/trunk/lib/Driver/Tools.cpp Tue Mar  1 17:16:44 2016
@@ -386,9 +386,71 @@ void Clang::AddPreprocessingOptions(Comp
   // wonky, but we include looking for .gch so we can support seamless
   // replacement into a build system already set up to be generating
   // .gch files.
+  int YcIndex = -1, YuIndex = -1;
+  {
+    int AI = -1;
+    const Arg *YcArg = Args.getLastArg(options::OPT__SLASH_Yc);
+    const Arg *YuArg = Args.getLastArg(options::OPT__SLASH_Yu);
+    for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) {
+      // Walk the whole i_Group and skip non "-include" flags so that the index
+      // here matches the index in the next loop below.
+      ++AI;
+      if (!A->getOption().matches(options::OPT_include))
+        continue;
+      if (YcArg && strcmp(A->getValue(), YcArg->getValue()) == 0)
+        YcIndex = AI;
+      if (YuArg && strcmp(A->getValue(), YuArg->getValue()) == 0)
+        YuIndex = AI;
+    }
+  }
+  if (isa<PrecompileJobAction>(JA) && YcIndex != -1) {
+    Driver::InputList Inputs;
+    D.BuildInputs(getToolChain(), C.getArgs(), Inputs);
+    assert(Inputs.size() == 1 && "Need one input when building pch");
+    CmdArgs.push_back(Args.MakeArgString(Twine("-find-pch-source=") +
+                                         Inputs[0].second->getValue()));
+  }
+
   bool RenderedImplicitInclude = false;
+  int AI = -1;
   for (const Arg *A : Args.filtered(options::OPT_clang_i_Group)) {
-    if (A->getOption().matches(options::OPT_include)) {
+    ++AI;
+
+    if (getToolChain().getDriver().IsCLMode()) {
+      // In clang-cl mode, /Ycfoo.h means that all code up to a foo.h 
+      // include is compiled into foo.h, and everything after goes into
+      // the .obj file. /Yufoo.h means that all includes prior to and including
+      // foo.h are completely skipped and replaced with a use of the pch file
+      // for foo.h.  (Each flag can have at most one value, multiple /Yc flags
+      // just mean that the last one wins.)  If /Yc and /Yu are both present
+      // and refer to the same file, /Yc wins.
+      // Note that OPT__SLASH_FI gets mapped to OPT_include.
+      // FIXME: The code here assumes that /Yc and /Yu refer to the same file.
+      // cl.exe seems to support both flags with different values, but that
+      // seems strange (which flag does /Fp now refer to?), so don't implement
+      // that until someone needs that.
+      int PchIndex = YcIndex != -1 ? YcIndex : YuIndex;
+      if (PchIndex != -1) {
+        if (isa<PrecompileJobAction>(JA)) {
+          // When building the pch, skip all includes after the pch.
+          assert(YcIndex != -1 && PchIndex == YcIndex);
+          if (AI >= YcIndex)
+            continue;
+        } else {
+          // When using the pch, skip all includes prior to the pch.
+          if (AI < PchIndex)
+            continue;
+          if (AI == PchIndex) {
+            A->claim();
+            CmdArgs.push_back("-include-pch");
+            CmdArgs.push_back(
+                Args.MakeArgString(D.GetClPchPath(C, A->getValue())));
+            continue;
+          }
+        }
+      }
+    } else if (A->getOption().matches(options::OPT_include)) {
+      // Handling of gcc-style gch precompiled headers.
       bool IsFirstImplicitInclude = !RenderedImplicitInclude;
       RenderedImplicitInclude = true;
 
@@ -5654,6 +5716,12 @@ void Clang::ConstructJob(Compilation &C,
         getCLFallback()->GetCommand(C, JA, Output, Inputs, Args, LinkingOutput);
     C.addCommand(llvm::make_unique<FallbackCommand>(
         JA, *this, Exec, CmdArgs, Inputs, std::move(CLCommand)));
+  } else if (Args.hasArg(options::OPT__SLASH_fallback) &&
+             isa<PrecompileJobAction>(JA)) {
+    // In /fallback builds, run the main compilation even if the pch generation
+    // fails, so that the main compilation's fallback to cl.exe runs.
+    C.addCommand(llvm::make_unique<ForceSuccessCommand>(JA, *this, Exec,
+                                                        CmdArgs, Inputs));
   } else {
     C.addCommand(llvm::make_unique<Command>(JA, *this, Exec, CmdArgs, Inputs));
   }

Modified: cfe/trunk/lib/Frontend/CompilerInstance.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInstance.cpp?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInstance.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInstance.cpp Tue Mar  1 17:16:44 2016
@@ -712,15 +712,18 @@ std::unique_ptr<llvm::raw_pwrite_stream>
 // Initialization Utilities
 
 bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input){
-  return InitializeSourceManager(Input, getDiagnostics(),
-                                 getFileManager(), getSourceManager(), 
-                                 getFrontendOpts());
+  return InitializeSourceManager(
+      Input, getDiagnostics(), getFileManager(), getSourceManager(),
+      hasPreprocessor() ? &getPreprocessor().getHeaderSearchInfo() : nullptr,
+      getFrontendOpts());
 }
 
+// static
 bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input,
                                                DiagnosticsEngine &Diags,
                                                FileManager &FileMgr,
                                                SourceManager &SourceMgr,
+                                               HeaderSearch *HS,
                                                const FrontendOptions &Opts) {
   SrcMgr::CharacteristicKind
     Kind = Input.isSystem() ? SrcMgr::C_System : SrcMgr::C_User;
@@ -737,7 +740,32 @@ bool CompilerInstance::InitializeSourceM
 
   // Figure out where to get and map in the main file.
   if (InputFile != "-") {
-    const FileEntry *File = FileMgr.getFile(InputFile, /*OpenFile=*/true);
+    const FileEntry *File;
+    if (Opts.FindPchSource.empty()) {
+      File = FileMgr.getFile(InputFile, /*OpenFile=*/true);
+    } else {
+      // When building a pch file in clang-cl mode, the .h file is built as if
+      // it was included by a cc file.  Since the driver doesn't know about
+      // all include search directories, the frontend must search the input
+      // file through HeaderSearch here, as if it had been included by the
+      // cc file at Opts.FindPchSource.
+      const FileEntry *FindFile = FileMgr.getFile(Opts.FindPchSource);
+      if (!FindFile) {
+        Diags.Report(diag::err_fe_error_reading) << Opts.FindPchSource;
+        return false;
+      }
+      const DirectoryLookup *UnusedCurDir;
+      SmallVector<std::pair<const FileEntry *, const DirectoryEntry *>, 16>
+          Includers;
+      Includers.push_back(std::make_pair(FindFile, FindFile->getDir()));
+      File = HS->LookupFile(InputFile, SourceLocation(), /*isAngled=*/false,
+                            /*FromDir=*/nullptr,
+                            /*CurDir=*/UnusedCurDir, Includers,
+                            /*SearchPath=*/nullptr,
+                            /*RelativePath=*/nullptr,
+                            /*RequestingModule=*/nullptr,
+                            /*SuggestedModule=*/nullptr, /*SkipCache=*/true);
+    }
     if (!File) {
       Diags.Report(diag::err_fe_error_reading) << InputFile;
       return false;

Modified: cfe/trunk/lib/Frontend/CompilerInvocation.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInvocation.cpp?rev=262420&r1=262419&r2=262420&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInvocation.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp Tue Mar  1 17:16:44 2016
@@ -1107,6 +1107,7 @@ static InputKind ParseFrontendArgs(Front
     = Args.getLastArgValue(OPT_foverride_record_layout_EQ);
   Opts.AuxTriple =
       llvm::Triple::normalize(Args.getLastArgValue(OPT_aux_triple));
+  Opts.FindPchSource = Args.getLastArgValue(OPT_find_pch_source_EQ);
 
   if (const Arg *A = Args.getLastArg(OPT_arcmt_check,
                                      OPT_arcmt_modify,

Added: cfe/trunk/test/Driver/Inputs/pchfile.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/Inputs/pchfile.cpp?rev=262420&view=auto
==============================================================================
    (empty)

Added: cfe/trunk/test/Driver/Inputs/pchfile.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/Inputs/pchfile.h?rev=262420&view=auto
==============================================================================
--- cfe/trunk/test/Driver/Inputs/pchfile.h (added)
+++ cfe/trunk/test/Driver/Inputs/pchfile.h Tue Mar  1 17:16:44 2016
@@ -0,0 +1,3 @@
+#if defined(ERR_HEADER)
+#error nope1
+#endif

Added: cfe/trunk/test/Driver/cl-pch-errorhandling.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/cl-pch-errorhandling.cpp?rev=262420&view=auto
==============================================================================
--- cfe/trunk/test/Driver/cl-pch-errorhandling.cpp (added)
+++ cfe/trunk/test/Driver/cl-pch-errorhandling.cpp Tue Mar  1 17:16:44 2016
@@ -0,0 +1,15 @@
+// Note: %s and %S must be preceded by --, otherwise it may be interpreted as a
+// command-line option, e.g. on Mac where %s is commonly under /Users.
+
+// /Yc but pch generation fails => main file not compiled
+// This is a separate file since executing this failure path requires
+// code generation, which makes this test require an x86 backend.
+// REQUIRES: x86-registered-target
+
+// RUN: not %clang_cl -internal-enable-pch -Werror /Yc%S/Inputs/pchfile.h /FI%S/Inputs/pchfile.h /c -DERR_HEADER -- %s 2>&1 \
+// RUN:   | FileCheck %s
+
+// CHECK: nope1
+// CHECK-NOT: nope2
+
+#error nope2

Added: cfe/trunk/test/Driver/cl-pch-search.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/cl-pch-search.cpp?rev=262420&view=auto
==============================================================================
--- cfe/trunk/test/Driver/cl-pch-search.cpp (added)
+++ cfe/trunk/test/Driver/cl-pch-search.cpp Tue Mar  1 17:16:44 2016
@@ -0,0 +1,6 @@
+// Note: %s and %S must be preceded by --, otherwise it may be interpreted as a
+// command-line option, e.g. on Mac where %s is commonly under /Users.
+
+// REQUIRES: x86-registered-target
+// Check that pchfile.h next to to pchfile.cc is found correctly.
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c /Fo%t.obj /Fp%t.pch -- %S/Inputs/pchfile.cpp 

Added: cfe/trunk/test/Driver/cl-pch.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/cl-pch.c?rev=262420&view=auto
==============================================================================
--- cfe/trunk/test/Driver/cl-pch.c (added)
+++ cfe/trunk/test/Driver/cl-pch.c Tue Mar  1 17:16:44 2016
@@ -0,0 +1,45 @@
+// Note: %s and %S must be preceded by --, otherwise it may be interpreted as a
+// command-line option, e.g. on Mac where %s is commonly under /Users.
+
+// The main test for clang-cl pch handling is cl-pch.cpp.  This file only checks
+// a few things for .c inputs.
+
+// /Yc with a .c file should build a c pch file.
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC %s
+// CHECK-YC: cc1
+// CHECK-YC: -emit-pch
+// CHECK-YC: -o
+// CHECK-YC: pchfile.pch
+// CHECK-YC: -x
+// CHECK-YC: "c"
+
+// But not if /TP changes the input language to C++.
+// RUN: %clang_cl /TP -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCTP %s
+// CHECK-YCTP: cc1
+// CHECK-YCTP: -emit-pch
+// CHECK-YCTP: -o
+// CHECK-YCTP: pchfile.pch
+// CHECK-YCTP: -x
+// CHECK-YCTP: "c++"
+
+// Except if a later /TC changes it back.
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCTPTC %s
+// CHECK-YCTPTC: cc1
+// CHECK-YCTPTC: -emit-pch
+// CHECK-YCTPTC: -o
+// CHECK-YCTPTC: pchfile.pch
+// CHECK-YCTPTC: -x
+// CHECK-YCTPTC: "c"
+
+// Also check lower-case /Tp flag.
+// RUN: %clang_cl -internal-enable-pch -Werror /Tp%s /Ycpchfile.h /FIpchfile.h /c -### 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCTp %s
+// CHECK-YCTp: cc1
+// CHECK-YCTp: -emit-pch
+// CHECK-YCTp: -o
+// CHECK-YCTp: pchfile.pch
+// CHECK-YCTp: -x
+// CHECK-YCTp: "c++"

Added: cfe/trunk/test/Driver/cl-pch.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/cl-pch.cpp?rev=262420&view=auto
==============================================================================
--- cfe/trunk/test/Driver/cl-pch.cpp (added)
+++ cfe/trunk/test/Driver/cl-pch.cpp Tue Mar  1 17:16:44 2016
@@ -0,0 +1,309 @@
+// Note: %s and %S must be preceded by --, otherwise it may be interpreted as a
+// command-line option, e.g. on Mac where %s is commonly under /Users.
+
+// /Yc
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC %s
+// 1. Build .pch file.
+// CHECK-YC: cc1
+// CHECK-YC: -emit-pch
+// CHECK-YC: -o
+// CHECK-YC: pchfile.pch
+// CHECK-YC: -x
+// CHECK-YC: "c++"
+// 2. Use .pch file.
+// CHECK-YC: cc1
+// CHECK-YC: -emit-obj
+// CHECK-YC: -include-pch
+// CHECK-YC: pchfile.pch
+
+// /Yc /Fo
+// /Fo overrides the .obj output filename, but not the .pch filename
+// RUN: %clang_cl -internal-enable-pch -Werror /Fomyobj.obj /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCO %s
+// 1. Build .pch file.
+// CHECK-YCO: cc1
+// CHECK-YCO: -emit-pch
+// CHECK-YCO: -o
+// CHECK-YCO: pchfile.pch
+// 2. Use .pch file.
+// CHECK-YCO: cc1
+// CHECK-YCO: -emit-obj
+// CHECK-YCO: -include-pch
+// CHECK-YCO: pchfile.pch
+// CHECK-YCO: -o
+// CHECK-YCO: myobj.obj
+
+// /Yc /Y-
+// /Y- disables pch generation
+// RUN: %clang_cl -internal-enable-pch -Werror /Y- /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-Y_ %s
+// CHECK-YC-Y_-NOT: -emit-pch
+// CHECK-YC-Y_-NOT: -include-pch
+
+// /Yu
+// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU %s
+// Use .pch file, but don't build it.
+// CHECK-YU-NOT: -emit-pch
+// CHECK-YU: cc1
+// CHECK-YU: -emit-obj
+// CHECK-YU: -include-pch
+// CHECK-YU: pchfile.pch
+
+// /Yu /Y-
+// RUN: %clang_cl -internal-enable-pch -Werror /Y- /Yupchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-Y_ %s
+// CHECK-YU-Y_-NOT: -emit-pch
+// CHECK-YU-Y_-NOT: -include-pch
+
+// /Yc /Yu -- /Yc overrides /Yc if they both refer to the same file
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /Yupchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-YU %s
+// 1. Build .pch file.
+// CHECK-YC-YU: cc1
+// CHECK-YC-YU: -emit-pch
+// CHECK-YC-YU: -o
+// CHECK-YC-YU: pchfile.pch
+// 2. Use .pch file.
+// CHECK-YC-YU: cc1
+// CHECK-YC-YU: -emit-obj
+// CHECK-YC-YU: -include-pch
+// CHECK-YC-YU: pchfile.pch
+
+// If /Yc /Yu refer to different files, semantics are pretty wonky.  Since this
+// doesn't seem like something that's important in practice, just punt for now.
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycfoo1.h /Yufoo2.h /FIfoo1.h /FIfoo2.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-YU-MISMATCH %s
+// CHECK-YC-YU-MISMATCH: error: support for '/Yc' and '/Yu' with different filenames not implemented yet; flags ignored
+
+// Similarly, punt on /Yc with more than one input file.
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycfoo1.h /FIfoo1.h /c -### -- %s %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-MULTIINPUT %s
+// CHECK-YC-MULTIINPUT: error: support for '/Yc' with more than one source file not implemented yet; flag ignored
+
+// /Yc /Yu /Y-
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /Yupchfile.h /FIpchfile.h /Y- /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-YU-Y_ %s
+// CHECK-YC-YU-Y_-NOT: -emit-pch
+// CHECK-YC-YU-Y_-NOT: -include-pch
+
+// Test computation of pch filename in various cases.
+
+// /Yu /Fpout.pch => out.pch is filename
+// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FIpchfile.h /Fpout.pch /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YUFP1 %s
+// Use .pch file, but don't build it.
+// CHECK-YUFP1: -include-pch
+// CHECK-YUFP1: out.pch
+
+// /Yu /Fpout => out.pch is filename (.pch gets added if no extension present)
+// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FIpchfile.h /Fpout.pch /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YUFP2 %s
+// Use .pch file, but don't build it.
+// CHECK-YUFP2: -include-pch
+// CHECK-YUFP2: out.pch
+
+// /Yu /Fpout.bmp => out.bmp is filename (.pch not added when extension present)
+// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FIpchfile.h /Fpout.bmp /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YUFP3 %s
+// Use .pch file, but don't build it.
+// CHECK-YUFP3: -include-pch
+// CHECK-YUFP3: out.bmp
+
+// /Yusub/dir.h => sub/dir.pch
+// RUN: %clang_cl -internal-enable-pch -Werror /Yusub/pchfile.h /FIsub/pchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YUFP4 %s
+// Use .pch file, but don't build it.
+// CHECK-YUFP4: -include-pch
+// CHECK-YUFP4: sub/pchfile.pch
+
+// /Yudir.h /Isub => dir.pch
+// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FIpchfile.h /Isub /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YUFP5 %s
+// Use .pch file, but don't build it.
+// CHECK-YUFP5: -include-pch
+// CHECK-YUFP5: pchfile.pch
+
+// FIXME: /Fpdir: use dir/VCx0.pch when dir is directory, where x is major MSVS
+// version in use.
+
+// Spot-check one use of /Fp with /Yc too, else trust the /Yu test cases above
+// also all assume to /Yc.
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /Fpsub/file.pch /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCFP %s
+// 1. Build .pch file.
+// CHECK-YCFP: cc1
+// CHECK-YCFP: -emit-pch
+// CHECK-YCFP: -o
+// CHECK-YCFP: sub/file.pch
+// 2. Use .pch file.
+// CHECK-YCFP: cc1
+// CHECK-YCFP: -emit-obj
+// CHECK-YCFP: -include-pch
+// CHECK-YCFP: sub/file.pch
+
+// /Ycfoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h
+// => foo1 and foo2 go into pch, foo3 into main compilation
+// /Yc
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycfoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCFIFIFI %s
+// 1. Build .pch file: Includes foo1.h (but NOT foo3.h) and compiles foo2.h
+// CHECK-YCFIFIFI: cc1
+// CHECK-YCFIFIFI: -emit-pch
+// CHECK-YCFIFIFI: -include
+// CHECK-YCFIFIFI: foo1.h
+// CHECK-YCFIFIFI-NOT: foo2.h
+// CHECK-YCFIFIFI-NOT: foo3.h
+// CHECK-YCFIFIFI: -o
+// CHECK-YCFIFIFI: foo2.pch
+// CHECK-YCFIFIFI: -x
+// CHECK-YCFIFIFI: "c++"
+// CHECK-YCFIFIFI: foo2.h
+// 2. Use .pch file: Inlucdes foo2.pch and foo3.h
+// CHECK-YCFIFIFI: cc1
+// CHECK-YCFIFIFI: -emit-obj
+// CHECK-YCFIFIFI-NOT: foo1.h
+// CHECK-YCFIFIFI-NOT: foo2.h
+// CHECK-YCFIFIFI: -include-pch
+// CHECK-YCFIFIFI: foo2.pch
+// CHECK-YCFIFIFI: -include
+// CHECK-YCFIFIFI: foo3.h
+
+// /Yucfoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h
+// => foo1 foo2 filtered out, foo3 into main compilation
+// RUN: %clang_cl -internal-enable-pch -Werror /Yufoo2.h /FIfoo1.h /FIfoo2.h /FIfoo3.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YUFIFIFI %s
+// Use .pch file, but don't build it.
+// CHECK-YUFIFIFI-NOT: -emit-pch
+// CHECK-YUFIFIFI: cc1
+// CHECK-YUFIFIFI: -emit-obj
+// CHECK-YUFIFIFI-NOT: foo1.h
+// CHECK-YUFIFIFI-NOT: foo2.h
+// CHECK-YUFIFIFI: -include-pch
+// CHECK-YUFIFIFI: foo2.pch
+// CHECK-YUFIFIFI: -include
+// CHECK-YUFIFIFI: foo3.h
+
+// FIXME: Implement support for /Ycfoo.h / /Yufoo.h without /FIfoo.h
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycfoo.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-NOFI %s
+// CHECK-YC-NOFI: error: support for '/Yc' without a corresponding /FI flag not implemented yet; flag ignored
+// RUN: %clang_cl -internal-enable-pch -Werror /Yufoo.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-NOFI %s
+// CHECK-YU-NOFI: error: support for '/Yu' without a corresponding /FI flag not implemented yet; flag ignored
+
+// /Yc and /FI relative to /I paths...
+// The rules are:
+// Yu/Yc and FI parameter must match exactly, else it's not found
+// Must match literally exactly: /FI./foo.h /Ycfoo.h does _not_ work.
+// However, the path can be relative to /I paths.
+// FIXME: Update the error messages below once /FI is no longer required, but
+// these test cases all should stay failures as they fail with cl.exe.
+
+// Check that ./ isn't canonicalized away.
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FI./pchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-I1 %s
+// CHECK-YC-I1: support for '/Yc' without a corresponding /FI flag not implemented yet; flag ignored
+
+// Check that ./ isn't canonicalized away.
+// RUN: %clang_cl -internal-enable-pch -Werror /Yc./pchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-I2 %s
+// CHECK-YC-I2: support for '/Yc' without a corresponding /FI flag not implemented yet; flag ignored
+
+// With an actual /I argument.
+// RUN: %clang_cl -internal-enable-pch -Werror /Ifoo /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-I3 %s
+// 1. This writes pchfile.pch into the root dir, even if this will pick up
+//    foo/pchfile.h
+// CHECK-YC-I3: cc1
+// CHECK-YC-I3: -emit-pch
+// CHECK-YC-I3: -o
+// CHECK-YC-I3: pchfile.pch
+// 2. Use .pch file.
+// CHECK-YC-I3: cc1
+// CHECK-YC-I3: -emit-obj
+// CHECK-YC-I3: -include-pch
+// CHECK-YC-I3: pchfile.pch
+
+// Check that ./ isn't canonicalized away for /Yu either.
+// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FI./pchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-I1 %s
+// CHECK-YU-I1: support for '/Yu' without a corresponding /FI flag not implemented yet; flag ignored
+
+// But /FIfoo/bar.h /Ycfoo\bar.h does work, as does /FIfOo.h /Ycfoo.H
+// FIXME: This part isn't implemented yet. The following two tests should not
+// show an error but do regular /Yu handling.
+// RUN: %clang_cl -internal-enable-pch -Werror /YupchFILE.h /FI./pchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-CASE %s
+// CHECK-YU-CASE: support for '/Yu' without a corresponding /FI flag not implemented yet; flag ignored
+// RUN: %clang_cl -internal-enable-pch -Werror /Yu./pchfile.h /FI.\pchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-SLASH %s
+// CHECK-YU-SLASH: support for '/Yu' without a corresponding /FI flag not implemented yet; flag ignored
+
+// cl.exe warns on multiple /Yc, /Yu, /Fp arguments, but clang-cl silently just
+// uses the last one.  This is true for e.g. /Fo too, so not warning on this
+// is self-consistent with clang-cl's flag handling.
+
+// Interaction with /fallback
+
+// /Yc /fallback => /Yc not passed on (but /FI is)
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /Fpfoo.pch /fallback /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YC-FALLBACK %s
+// Note that in /fallback builds, if creation of the pch fails the main compile
+// does still run so that /fallback can have an effect (this part is not tested)
+// CHECK-YC-FALLBACK: cc1
+// CHECK-YC-FALLBACK: -emit-obj
+// CHECK-YC-FALLBACK: -include-pch
+// CHECK-YC-FALLBACK: foo.pch
+// CHECK-YC-FALLBACK: ||
+// CHECK-YC-FALLBACK: cl.exe
+// CHECK-YC-FALLBACK-NOT: -include-pch
+// CHECK-YC-FALLBACK-NOT: /Ycpchfile.h
+// CHECK-YC-FALLBACK: /FIpchfile.h
+// CHECK-YC-FALLBACK-NOT: /Fpfoo.pch
+
+// /Yu /fallback => /Yu not passed on (but /FI is)
+// RUN: %clang_cl -internal-enable-pch -Werror /Yupchfile.h /FIpchfile.h /Fpfoo.pch /fallback /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YU-FALLBACK %s
+// CHECK-YU-FALLBACK-NOT: -emit-pch
+// CHECK-YU-FALLBACK: cc1
+// CHECK-YU-FALLBACK: -emit-obj
+// CHECK-YU-FALLBACK: -include-pch
+// CHECK-YU-FALLBACK: foo.pch
+// CHECK-YU-FALLBACK: ||
+// CHECK-YU-FALLBACK: cl.exe
+// CHECK-YU-FALLBACK-NOT: -include-pch
+// CHECK-YU-FALLBACK-NOT: /Yupchfile.h
+// CHECK-YU-FALLBACK: /FIpchfile.h
+// CHECK-YU-FALLBACK-NOT: /Fpfoo.pch
+
+// /FI without /Yu => pch file not used, even if it exists (different from
+// -include, which picks up .gch files if they exist).
+// RUN: touch %t.pch
+// RUN: %clang_cl -internal-enable-pch -Werror /FI%t.pch /Fp%t.pch /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-FI %s
+// CHECK-FI-NOT: -include-pch
+// CHECK-FI: -include
+
+// Test interaction of /Yc with language mode flags.
+
+// If /TC changes the input language to C, a c pch file should be produced.
+// RUN: %clang_cl /TC -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c -### -- %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCTC %s
+// CHECK-YCTC: cc1
+// CHECK-YCTC: -emit-pch
+// CHECK-YCTC: -o
+// CHECK-YCTC: pchfile.pch
+// CHECK-YCTC: -x
+// CHECK-YCTP: "c"
+
+// Also check lower-case /Tc variant.
+// RUN: %clang_cl -internal-enable-pch -Werror /Ycpchfile.h /FIpchfile.h /c -### /Tc%s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHECK-YCTc %s
+// CHECK-YCTc: cc1
+// CHECK-YCTc: -emit-pch
+// CHECK-YCTc: -o
+// CHECK-YCTc: pchfile.pch
+// CHECK-YCTc: -x
+// CHECK-YCTc: "c"




More information about the cfe-commits mailing list