Skip to content

Commit

Permalink
clang-cl: implement /fallback mode
Browse files Browse the repository at this point in the history
When this flag is enabled, clang-cl falls back to cl.exe if it
cannot compile the code itself for some reason.

The idea is to use this to help build projects that almost compile
with clang-cl, except for some files that can then be built with
the fallback mechanism.

Differential Revision: http://llvm-reviews.chandlerc.com/D1711

llvm-svn: 191034
  • Loading branch information
zmodem committed Sep 19, 2013
1 parent 70fefd5 commit 87cfa71
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 9 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Driver/CLCompatOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ def _SLASH_Zs : CLFlag<"Zs">, HelpText<"Syntax-check only">,

def _SLASH_M_Group : OptionGroup<"</M group>">, Group<cl_compile_Group>;

def _SLASH_fallback : CLCompileFlag<"fallback">,
HelpText<"Fall back to cl.exe if clang-cl fails to compile">;
def _SLASH_Fe : CLJoined<"Fe">,
HelpText<"Set output executable file or directory (ends in / or \\)">,
MetaVarName<"<file or directory>">;
Expand Down
37 changes: 31 additions & 6 deletions clang/include/clang/Driver/Job.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#define CLANG_DRIVER_JOB_H_

#include "clang/Basic/LLVM.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Option/Option.h"

Expand All @@ -31,6 +32,7 @@ class Job {
public:
enum JobClass {
CommandClass,
FallbackCommandClass,
JobListClass
};

Expand All @@ -54,8 +56,8 @@ class Job {
bool Quote, bool CrashReport = false) const = 0;
};

/// Command - An executable path/name and argument vector to
/// execute.
/// Command - An executable path/name and argument vector to
/// execute.
class Command : public Job {
/// Source - The action which caused the creation of this job.
const Action &Source;
Expand All @@ -77,8 +79,8 @@ class Command : public Job {
virtual void Print(llvm::raw_ostream &OS, const char *Terminator,
bool Quote, bool CrashReport = false) const;

int Execute(const StringRef **Redirects, std::string *ErrMsg,
bool *ExecutionFailed) const;
virtual int Execute(const StringRef **Redirects, std::string *ErrMsg,
bool *ExecutionFailed) const;

/// getSource - Return the Action which caused the creation of this job.
const Action &getSource() const { return Source; }
Expand All @@ -89,11 +91,34 @@ class Command : public Job {
const llvm::opt::ArgStringList &getArguments() const { return Arguments; }

static bool classof(const Job *J) {
return J->getKind() == CommandClass;
return J->getKind() == CommandClass ||
J->getKind() == FallbackCommandClass;
}
};

/// JobList - A sequence of jobs to perform.
/// Like Command, but with a fallback which is executed in case
/// the primary command crashes.
class FallbackCommand : public Command {
public:
FallbackCommand(const Action &Source_, const Tool &Creator_,
const char *Executable_, const ArgStringList &Arguments_,
Command *Fallback_);

virtual void Print(llvm::raw_ostream &OS, const char *Terminator,
bool Quote, bool CrashReport = false) const;

virtual int Execute(const StringRef **Redirects, std::string *ErrMsg,
bool *ExecutionFailed) const;

static bool classof(const Job *J) {
return J->getKind() == FallbackCommandClass;
}

private:
OwningPtr<Command> Fallback;
};

/// JobList - A sequence of jobs to perform.
class JobList : public Job {
public:
typedef SmallVector<Job*, 4> list_type;
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Driver/Tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ class Tool {
virtual bool hasGoodDiagnostics() const { return false; }

/// ConstructJob - Construct jobs to perform the action \p JA,
/// writing to \p Output and with \p Inputs.
/// writing to \p Output and with \p Inputs, and add the jobs to
/// \p C.
///
/// \param TCArgs - The argument list for this toolchain, with any
/// tool chain specific translations applied.
Expand Down
37 changes: 37 additions & 0 deletions clang/lib/Driver/Job.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,43 @@ int Command::Execute(const StringRef **Redirects, std::string *ErrMsg,
/*memoryLimit*/ 0, ErrMsg, ExecutionFailed);
}

FallbackCommand::FallbackCommand(const Action &Source_, const Tool &Creator_,
const char *Executable_,
const ArgStringList &Arguments_,
Command *Fallback_)
: Command(Source_, Creator_, Executable_, Arguments_), Fallback(Fallback_) {
}

void FallbackCommand::Print(raw_ostream &OS, const char *Terminator,
bool Quote, bool CrashReport) const {
Command::Print(OS, "", Quote, CrashReport);
OS << " ||";
Fallback->Print(OS, Terminator, Quote, CrashReport);
}

static bool ShouldFallback(int ExitCode) {
// FIXME: We really just want to fall back for internal errors, such
// as when some symbol cannot be mangled, when we should be able to
// parse something but can't, etc.
return ExitCode != 0;
}

int FallbackCommand::Execute(const StringRef **Redirects, std::string *ErrMsg,
bool *ExecutionFailed) const {
int PrimaryStatus = Command::Execute(Redirects, ErrMsg, ExecutionFailed);
if (!ShouldFallback(PrimaryStatus))
return PrimaryStatus;

// Clear ExecutionFailed and ErrMsg before falling back.
if (ErrMsg)
ErrMsg->clear();
if (ExecutionFailed)
*ExecutionFailed = false;

int SecondaryStatus = Fallback->Execute(Redirects, ErrMsg, ExecutionFailed);
return SecondaryStatus;
}

JobList::JobList() : Job(JobListClass) {}

JobList::~JobList() {
Expand Down
77 changes: 76 additions & 1 deletion clang/lib/Driver/Tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3565,7 +3565,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
}

// Finally add the compile command to the compilation.
C.addCommand(new Command(JA, *this, Exec, CmdArgs));
if (Args.hasArg(options::OPT__SLASH_fallback)) {
tools::visualstudio::Compile CL(getToolChain());
Command *CLCommand = CL.GetCommand(C, JA, Output, Inputs, Args,
LinkingOutput);
C.addCommand(new FallbackCommand(JA, *this, Exec, CmdArgs, CLCommand));
} else {
C.addCommand(new Command(JA, *this, Exec, CmdArgs));
}


// Handle the debug info splitting at object creation time if we're
// creating an object.
Expand Down Expand Up @@ -6638,3 +6646,70 @@ void visualstudio::Link::ConstructJob(Compilation &C, const JobAction &JA,
Args.MakeArgString(getToolChain().GetProgramPath("link.exe"));
C.addCommand(new Command(JA, *this, Exec, CmdArgs));
}

void visualstudio::Compile::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const ArgList &Args,
const char *LinkingOutput) const {
C.addCommand(GetCommand(C, JA, Output, Inputs, Args, LinkingOutput));
}

Command *visualstudio::Compile::GetCommand(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const ArgList &Args,
const char *LinkingOutput) const {
ArgStringList CmdArgs;
CmdArgs.push_back("/c"); // Compile only.
CmdArgs.push_back("/W0"); // No warnings.

// The goal is to be able to invoke this tool correctly based on
// any flag accepted by clang-cl.

// These are spelled the same way in clang and cl.exe,.
Args.AddAllArgs(CmdArgs, options::OPT_D, options::OPT_U);
Args.AddAllArgs(CmdArgs, options::OPT_I);
Args.AddLastArg(CmdArgs, options::OPT_O, options::OPT_O0);

// Flags for which clang-cl have an alias.
// FIXME: How can we ensure this stays in sync with relevant clang-cl options?

if (Arg *A = Args.getLastArg(options::OPT_frtti, options::OPT_fno_rtti))
CmdArgs.push_back(A->getOption().getID() == options::OPT_frtti ? "/GR"
: "/GR-");
if (Args.hasArg(options::OPT_fsyntax_only))
CmdArgs.push_back("/Zs");

// Flags that can simply be passed through.
Args.AddAllArgs(CmdArgs, options::OPT__SLASH_LD);
Args.AddAllArgs(CmdArgs, options::OPT__SLASH_LDd);

// The order of these flags is relevant, so pick the last one.
if (Arg *A = Args.getLastArg(options::OPT__SLASH_MD, options::OPT__SLASH_MDd,
options::OPT__SLASH_MT, options::OPT__SLASH_MTd))
A->render(Args, CmdArgs);


// Input filename.
assert(Inputs.size() == 1);
const InputInfo &II = Inputs[0];
assert(II.getType() == types::TY_C || II.getType() == types::TY_CXX);
CmdArgs.push_back(II.getType() == types::TY_C ? "/Tc" : "/Tp");
if (II.isFilename())
CmdArgs.push_back(II.getFilename());
else
II.getInputArg().renderAsInput(Args, CmdArgs);

// Output filename.
assert(Output.getType() == types::TY_Object);
const char *Fo = Args.MakeArgString(std::string("/Fo") +
Output.getFilename());
CmdArgs.push_back(Fo);

// FIXME: If we've put clang-cl as cl.exe on the path, we have a problem.
const char *Exec =
Args.MakeArgString(getToolChain().GetProgramPath("cl.exe"));

return new Command(JA, *this, Exec, CmdArgs);
}
24 changes: 23 additions & 1 deletion clang/lib/Driver/Tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace clang {
class ObjCRuntime;

namespace driver {
class Command;
class Driver;

namespace toolchains {
Expand Down Expand Up @@ -590,7 +591,7 @@ namespace dragonfly {

/// Visual studio tools.
namespace visualstudio {
class LLVM_LIBRARY_VISIBILITY Link : public Tool {
class LLVM_LIBRARY_VISIBILITY Link : public Tool {
public:
Link(const ToolChain &TC) : Tool("visualstudio::Link", "linker", TC) {}

Expand All @@ -603,6 +604,27 @@ namespace visualstudio {
const llvm::opt::ArgList &TCArgs,
const char *LinkingOutput) const;
};

class LLVM_LIBRARY_VISIBILITY Compile : public Tool {
public:
Compile(const ToolChain &TC) : Tool("visualstudio::Compile", "compiler", TC) {}

virtual bool hasIntegratedAssembler() const { return true; }
virtual bool hasIntegratedCPP() const { return true; }
virtual bool isLinkJob() const { return false; }

virtual void ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const llvm::opt::ArgList &TCArgs,
const char *LinkingOutput) const;

Command *GetCommand(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const llvm::opt::ArgList &TCArgs,
const char *LinkingOutput) const;
};
} // end namespace visualstudio

} // end namespace toolchains
Expand Down
22 changes: 22 additions & 0 deletions clang/test/Driver/cl-fallback.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Don't attempt slash switches on msys bash.
// REQUIRES: shell-preserves-root

// Note: %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.

// RUN: %clang_cl /fallback /Dfoo=bar /Ubaz /Ifoo /O0 /Ox /GR /GR- /LD /LDd \
// RUN: /MD /MDd /MTd /MT -### -- %s 2>&1 | FileCheck %s
// CHECK: ||
// CHECK: cl.exe
// CHECK: "/c"
// CHECK: "/W0"
// CHECK: "-D" "foo=bar"
// CHECK: "-U" "baz"
// CHECK: "-I" "foo"
// CHECK: "-O3"
// CHECK: "/GR-"
// CHECK: "/LD"
// CHECK: "/LDd"
// CHECK: "/MT"
// CHECK: "/Tc" "{{.*cl-fallback.c}}"
// CHECK: "/Fo{{.*cl-fallback.*.obj}}"

0 comments on commit 87cfa71

Please sign in to comment.