summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/Driver/Driver.cpp103
-rw-r--r--clang/lib/Driver/Job.cpp23
-rw-r--r--clang/lib/Driver/Tools.cpp70
-rw-r--r--clang/lib/Frontend/CompilerInstance.cpp36
-rw-r--r--clang/lib/Frontend/CompilerInvocation.cpp1
5 files changed, 227 insertions, 6 deletions
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index eaf29de2aaf..8965d3d6c3c 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -1452,6 +1452,66 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
}
}
+ // 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, DerivedArgList &Args,
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(Compilation &C, const JobAction &JA,
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(StringRef Prefix,
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 {
diff --git a/clang/lib/Driver/Job.cpp b/clang/lib/Driver/Job.cpp
index 22904e5398a..3eb23c9ee4c 100644
--- a/clang/lib/Driver/Job.cpp
+++ b/clang/lib/Driver/Job.cpp
@@ -297,6 +297,29 @@ int FallbackCommand::Execute(const StringRef **Redirects, std::string *ErrMsg,
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)
diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp
index a0fceb9672a..0f5c700ce61 100644
--- a/clang/lib/Driver/Tools.cpp
+++ b/clang/lib/Driver/Tools.cpp
@@ -386,9 +386,71 @@ void Clang::AddPreprocessingOptions(Compilation &C, const JobAction &JA,
// 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, const JobAction &JA,
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));
}
diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp
index 04a4c7dbe68..c02eb761041 100644
--- a/clang/lib/Frontend/CompilerInstance.cpp
+++ b/clang/lib/Frontend/CompilerInstance.cpp
@@ -712,15 +712,18 @@ std::unique_ptr<llvm::raw_pwrite_stream> CompilerInstance::createOutputFile(
// 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::InitializeSourceManager(const FrontendInputFile &Input,
// 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;
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index b23eb354375..9bb33e7a766 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1107,6 +1107,7 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
= 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,
OpenPOWER on IntegriCloud