Commit a77a17e2c2

Andrew Kelley <andrew@ziglang.org>
2019-02-24 18:53:28
add `zig cc` command to act like a C compiler
closes #490
1 parent 00d8f4a
cmake/Findclang.cmake
@@ -11,17 +11,28 @@ if(MSVC)
   find_package(CLANG REQUIRED CONFIG)
 
   set(CLANG_LIBRARIES
+      clangFrontendTool
+      clangCodeGen
       clangFrontend
       clangDriver
       clangSerialization
       clangSema
+      clangStaticAnalyzerFrontend
+      clangStaticAnalyzerCheckers
+      clangStaticAnalyzerCore
       clangAnalysis
+      clangASTMatchers
       clangAST
       clangParse
       clangSema
       clangBasic
       clangEdit
       clangLex
+      clangARCMigrate
+      clangRewriteFrontend
+      clangRewrite
+      clangCrossTU
+      clangIndex
   )
 
 else()
@@ -50,17 +61,28 @@ else()
       endif()
   endmacro(FIND_AND_ADD_CLANG_LIB)
 
+  FIND_AND_ADD_CLANG_LIB(clangFrontendTool)
+  FIND_AND_ADD_CLANG_LIB(clangCodeGen)
   FIND_AND_ADD_CLANG_LIB(clangFrontend)
   FIND_AND_ADD_CLANG_LIB(clangDriver)
   FIND_AND_ADD_CLANG_LIB(clangSerialization)
   FIND_AND_ADD_CLANG_LIB(clangSema)
+  FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerFrontend)
+  FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCheckers)
+  FIND_AND_ADD_CLANG_LIB(clangStaticAnalyzerCore)
   FIND_AND_ADD_CLANG_LIB(clangAnalysis)
+  FIND_AND_ADD_CLANG_LIB(clangASTMatchers)
   FIND_AND_ADD_CLANG_LIB(clangAST)
   FIND_AND_ADD_CLANG_LIB(clangParse)
   FIND_AND_ADD_CLANG_LIB(clangSema)
   FIND_AND_ADD_CLANG_LIB(clangBasic)
   FIND_AND_ADD_CLANG_LIB(clangEdit)
   FIND_AND_ADD_CLANG_LIB(clangLex)
+  FIND_AND_ADD_CLANG_LIB(clangARCMigrate)
+  FIND_AND_ADD_CLANG_LIB(clangRewriteFrontend)
+  FIND_AND_ADD_CLANG_LIB(clangRewrite)
+  FIND_AND_ADD_CLANG_LIB(clangCrossTU)
+  FIND_AND_ADD_CLANG_LIB(clangIndex)
 endif()
 
 include(FindPackageHandleStandardArgs)
src/main.cpp
@@ -32,6 +32,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
         "  build-lib [source]           create library from source or object files\n"
         "  build-obj [source]           create object from source or assembly\n"
         "  builtin                      show the source code of that @import(\"builtin\")\n"
+        "  cc                           C compiler\n"
         "  fmt                          parse files and render in canonical zig format\n"
         "  help                         show this usage information\n"
         "  id                           print the base64-encoded compiler id\n"
@@ -240,6 +241,8 @@ static bool get_cache_opt(CacheOpt opt, bool default_value) {
     zig_unreachable();
 }
 
+extern "C" int ZigClang_main(int argc, char **argv);
+
 int main(int argc, char **argv) {
     char *arg0 = argv[0];
     Error err;
@@ -257,6 +260,16 @@ int main(int argc, char **argv) {
         return 0;
     }
 
+    if (argc >= 2 && (strcmp(argv[1], "cc") == 0)) {
+        return ZigClang_main(argc - 1, argv + 1);
+    }
+    if (argc >= 2 && strcmp(argv[1], "-cc1") == 0) {
+        return ZigClang_main(argc, argv);
+    }
+    if (argc >= 2 && strcmp(argv[1], "-cc1as") == 0) {
+        return ZigClang_main(argc, argv);
+    }
+
     // Must be before all os.hpp function calls.
     os_init();
 
src/zig_clang_cc1_main.cpp
@@ -0,0 +1,237 @@
+//===-- cc1_main.cpp - Clang CC1 Compiler Frontend ------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This is the entry point to the clang -cc1 functionality, which implements the
+// core compiler functionality along with a number of additional tools for
+// demonstration and testing purposes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Option/Arg.h"
+#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
+#include "clang/Config/config.h"
+#include "clang/Basic/Stack.h"
+#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Frontend/TextDiagnosticBuffer.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Frontend/Utils.h"
+#include "clang/FrontendTool/Utils.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Config/llvm-config.h"
+#include "llvm/LinkAllPasses.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstdio>
+
+#ifdef CLANG_HAVE_RLIMITS
+#include <sys/resource.h>
+#endif
+
+using namespace clang;
+using namespace llvm::opt;
+
+//===----------------------------------------------------------------------===//
+// Main driver
+//===----------------------------------------------------------------------===//
+
+static void LLVMErrorHandler(void *UserData, const std::string &Message,
+                             bool GenCrashDiag) {
+  DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
+
+  Diags.Report(diag::err_fe_error_backend) << Message;
+
+  // Run the interrupt handlers to make sure any special cleanups get done, in
+  // particular that we remove files registered with RemoveFileOnSignal.
+  llvm::sys::RunInterruptHandlers();
+
+  // We cannot recover from llvm errors.  When reporting a fatal error, exit
+  // with status 70 to generate crash diagnostics.  For BSD systems this is
+  // defined as an internal software error.  Otherwise, exit with status 1.
+  exit(GenCrashDiag ? 70 : 1);
+}
+
+#ifdef LINK_POLLY_INTO_TOOLS
+namespace polly {
+void initializePollyPasses(llvm::PassRegistry &Registry);
+}
+#endif
+
+#ifdef CLANG_HAVE_RLIMITS
+#if defined(__linux__) && defined(__PIE__)
+static size_t getCurrentStackAllocation() {
+  // If we can't compute the current stack usage, allow for 512K of command
+  // line arguments and environment.
+  size_t Usage = 512 * 1024;
+  if (FILE *StatFile = fopen("/proc/self/stat", "r")) {
+    // We assume that the stack extends from its current address to the end of
+    // the environment space. In reality, there is another string literal (the
+    // program name) after the environment, but this is close enough (we only
+    // need to be within 100K or so).
+    unsigned long StackPtr, EnvEnd;
+    // Disable silly GCC -Wformat warning that complains about length
+    // modifiers on ignored format specifiers. We want to retain these
+    // for documentation purposes even though they have no effect.
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat"
+#endif
+    if (fscanf(StatFile,
+               "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %*lu "
+               "%*lu %*ld %*ld %*ld %*ld %*ld %*ld %*llu %*lu %*ld %*lu %*lu "
+               "%*lu %*lu %lu %*lu %*lu %*lu %*lu %*lu %*llu %*lu %*lu %*d %*d "
+               "%*u %*u %*llu %*lu %*ld %*lu %*lu %*lu %*lu %*lu %*lu %lu %*d",
+               &StackPtr, &EnvEnd) == 2) {
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+      Usage = StackPtr < EnvEnd ? EnvEnd - StackPtr : StackPtr - EnvEnd;
+    }
+    fclose(StatFile);
+  }
+  return Usage;
+}
+
+#include <alloca.h>
+
+LLVM_ATTRIBUTE_NOINLINE
+static void ensureStackAddressSpace() {
+  // Linux kernels prior to 4.1 will sometimes locate the heap of a PIE binary
+  // relatively close to the stack (they are only guaranteed to be 128MiB
+  // apart). This results in crashes if we happen to heap-allocate more than
+  // 128MiB before we reach our stack high-water mark.
+  //
+  // To avoid these crashes, ensure that we have sufficient virtual memory
+  // pages allocated before we start running.
+  size_t Curr = getCurrentStackAllocation();
+  const int kTargetStack = DesiredStackSize - 256 * 1024;
+  if (Curr < kTargetStack) {
+    volatile char *volatile Alloc =
+        static_cast<volatile char *>(alloca(kTargetStack - Curr));
+    Alloc[0] = 0;
+    Alloc[kTargetStack - Curr - 1] = 0;
+  }
+}
+#else
+static void ensureStackAddressSpace() {}
+#endif
+
+/// Attempt to ensure that we have at least 8MiB of usable stack space.
+static void ensureSufficientStack() {
+  struct rlimit rlim;
+  if (getrlimit(RLIMIT_STACK, &rlim) != 0)
+    return;
+
+  // Increase the soft stack limit to our desired level, if necessary and
+  // possible.
+  if (rlim.rlim_cur != RLIM_INFINITY &&
+      rlim.rlim_cur < rlim_t(DesiredStackSize)) {
+    // Try to allocate sufficient stack.
+    if (rlim.rlim_max == RLIM_INFINITY ||
+        rlim.rlim_max >= rlim_t(DesiredStackSize))
+      rlim.rlim_cur = DesiredStackSize;
+    else if (rlim.rlim_cur == rlim.rlim_max)
+      return;
+    else
+      rlim.rlim_cur = rlim.rlim_max;
+
+    if (setrlimit(RLIMIT_STACK, &rlim) != 0 ||
+        rlim.rlim_cur != DesiredStackSize)
+      return;
+  }
+
+  // We should now have a stack of size at least DesiredStackSize. Ensure
+  // that we can actually use that much, if necessary.
+  ensureStackAddressSpace();
+}
+#else
+static void ensureSufficientStack() {}
+#endif
+
+int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
+  ensureSufficientStack();
+
+  std::unique_ptr<CompilerInstance> Clang(new CompilerInstance());
+  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+
+  // Register the support for object-file-wrapped Clang modules.
+  auto PCHOps = Clang->getPCHContainerOperations();
+  PCHOps->registerWriter(llvm::make_unique<ObjectFilePCHContainerWriter>());
+  PCHOps->registerReader(llvm::make_unique<ObjectFilePCHContainerReader>());
+
+  // Initialize targets first, so that --version shows registered targets.
+  llvm::InitializeAllTargets();
+  llvm::InitializeAllTargetMCs();
+  llvm::InitializeAllAsmPrinters();
+  llvm::InitializeAllAsmParsers();
+
+#ifdef LINK_POLLY_INTO_TOOLS
+  llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
+  polly::initializePollyPasses(Registry);
+#endif
+
+  // Buffer diagnostics from argument parsing so that we can output them using a
+  // well formed diagnostic object.
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+  TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer;
+  DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer);
+  bool Success = CompilerInvocation::CreateFromArgs(
+      Clang->getInvocation(), Argv.begin(), Argv.end(), Diags);
+
+  // Infer the builtin include path if unspecified.
+  if (Clang->getHeaderSearchOpts().UseBuiltinIncludes &&
+      Clang->getHeaderSearchOpts().ResourceDir.empty())
+    Clang->getHeaderSearchOpts().ResourceDir =
+      CompilerInvocation::GetResourcesPath(Argv0, MainAddr);
+
+  // Create the actual diagnostics engine.
+  Clang->createDiagnostics();
+  if (!Clang->hasDiagnostics())
+    return 1;
+
+  // Set an error handler, so that any LLVM backend diagnostics go through our
+  // error handler.
+  llvm::install_fatal_error_handler(LLVMErrorHandler,
+                                  static_cast<void*>(&Clang->getDiagnostics()));
+
+  DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics());
+  if (!Success)
+    return 1;
+
+  // Execute the frontend actions.
+  Success = ExecuteCompilerInvocation(Clang.get());
+
+  // If any timers were active but haven't been destroyed yet, print their
+  // results now.  This happens in -disable-free mode.
+  llvm::TimerGroup::printAll(llvm::errs());
+
+  // Our error handler depends on the Diagnostics object, which we're
+  // potentially about to delete. Uninstall the handler now so that any
+  // later errors use the default handling behavior instead.
+  llvm::remove_fatal_error_handler();
+
+  // When running with -disable-free, don't do any destruction or shutdown.
+  if (Clang->getFrontendOpts().DisableFree) {
+    BuryPointer(std::move(Clang));
+    return !Success;
+  }
+
+  return !Success;
+}
+
src/zig_clang_cc1as_main.cpp
@@ -0,0 +1,573 @@
+//===-- cc1as_main.cpp - Clang Assembler  ---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This is the entry point to the clang -cc1as functionality, which implements
+// the direct interface to the LLVM MC based assembler.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/Driver/Options.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Frontend/Utils.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/MC/MCAsmBackend.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCCodeEmitter.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCObjectWriter.h"
+#include "llvm/MC/MCParser/MCAsmParser.h"
+#include "llvm/MC/MCParser/MCTargetAsmParser.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCTargetOptions.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+#include <system_error>
+using namespace clang;
+using namespace clang::driver;
+using namespace clang::driver::options;
+using namespace llvm;
+using namespace llvm::opt;
+
+namespace {
+
+/// Helper class for representing a single invocation of the assembler.
+struct AssemblerInvocation {
+  /// @name Target Options
+  /// @{
+
+  /// The name of the target triple to assemble for.
+  std::string Triple;
+
+  /// If given, the name of the target CPU to determine which instructions
+  /// are legal.
+  std::string CPU;
+
+  /// The list of target specific features to enable or disable -- this should
+  /// be a list of strings starting with '+' or '-'.
+  std::vector<std::string> Features;
+
+  /// The list of symbol definitions.
+  std::vector<std::string> SymbolDefs;
+
+  /// @}
+  /// @name Language Options
+  /// @{
+
+  std::vector<std::string> IncludePaths;
+  unsigned NoInitialTextSection : 1;
+  unsigned SaveTemporaryLabels : 1;
+  unsigned GenDwarfForAssembly : 1;
+  unsigned RelaxELFRelocations : 1;
+  unsigned DwarfVersion;
+  std::string DwarfDebugFlags;
+  std::string DwarfDebugProducer;
+  std::string DebugCompilationDir;
+  std::map<const std::string, const std::string> DebugPrefixMap;
+  llvm::DebugCompressionType CompressDebugSections =
+      llvm::DebugCompressionType::None;
+  std::string MainFileName;
+  std::string SplitDwarfFile;
+
+  /// @}
+  /// @name Frontend Options
+  /// @{
+
+  std::string InputFile;
+  std::vector<std::string> LLVMArgs;
+  std::string OutputPath;
+  enum FileType {
+    FT_Asm,  ///< Assembly (.s) output, transliterate mode.
+    FT_Null, ///< No output, for timing purposes.
+    FT_Obj   ///< Object file output.
+  };
+  FileType OutputType;
+  unsigned ShowHelp : 1;
+  unsigned ShowVersion : 1;
+
+  /// @}
+  /// @name Transliterate Options
+  /// @{
+
+  unsigned OutputAsmVariant;
+  unsigned ShowEncoding : 1;
+  unsigned ShowInst : 1;
+
+  /// @}
+  /// @name Assembler Options
+  /// @{
+
+  unsigned RelaxAll : 1;
+  unsigned NoExecStack : 1;
+  unsigned FatalWarnings : 1;
+  unsigned IncrementalLinkerCompatible : 1;
+
+  /// The name of the relocation model to use.
+  std::string RelocationModel;
+
+  /// @}
+
+public:
+  AssemblerInvocation() {
+    Triple = "";
+    NoInitialTextSection = 0;
+    InputFile = "-";
+    OutputPath = "-";
+    OutputType = FT_Asm;
+    OutputAsmVariant = 0;
+    ShowInst = 0;
+    ShowEncoding = 0;
+    RelaxAll = 0;
+    NoExecStack = 0;
+    FatalWarnings = 0;
+    IncrementalLinkerCompatible = 0;
+    DwarfVersion = 0;
+  }
+
+  static bool CreateFromArgs(AssemblerInvocation &Res,
+                             ArrayRef<const char *> Argv,
+                             DiagnosticsEngine &Diags);
+};
+
+}
+
+bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
+                                         ArrayRef<const char *> Argv,
+                                         DiagnosticsEngine &Diags) {
+  bool Success = true;
+
+  // Parse the arguments.
+  std::unique_ptr<OptTable> OptTbl(createDriverOptTable());
+
+  const unsigned IncludedFlagsBitmask = options::CC1AsOption;
+  unsigned MissingArgIndex, MissingArgCount;
+  InputArgList Args = OptTbl->ParseArgs(Argv, MissingArgIndex, MissingArgCount,
+                                        IncludedFlagsBitmask);
+
+  // Check for missing argument error.
+  if (MissingArgCount) {
+    Diags.Report(diag::err_drv_missing_argument)
+        << Args.getArgString(MissingArgIndex) << MissingArgCount;
+    Success = false;
+  }
+
+  // Issue errors on unknown arguments.
+  for (const Arg *A : Args.filtered(OPT_UNKNOWN)) {
+    auto ArgString = A->getAsString(Args);
+    std::string Nearest;
+    if (OptTbl->findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1)
+      Diags.Report(diag::err_drv_unknown_argument) << ArgString;
+    else
+      Diags.Report(diag::err_drv_unknown_argument_with_suggestion)
+          << ArgString << Nearest;
+    Success = false;
+  }
+
+  // Construct the invocation.
+
+  // Target Options
+  Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple));
+  Opts.CPU = Args.getLastArgValue(OPT_target_cpu);
+  Opts.Features = Args.getAllArgValues(OPT_target_feature);
+
+  // Use the default target triple if unspecified.
+  if (Opts.Triple.empty())
+    Opts.Triple = llvm::sys::getDefaultTargetTriple();
+
+  // Language Options
+  Opts.IncludePaths = Args.getAllArgValues(OPT_I);
+  Opts.NoInitialTextSection = Args.hasArg(OPT_n);
+  Opts.SaveTemporaryLabels = Args.hasArg(OPT_msave_temp_labels);
+  // Any DebugInfoKind implies GenDwarfForAssembly.
+  Opts.GenDwarfForAssembly = Args.hasArg(OPT_debug_info_kind_EQ);
+
+  if (const Arg *A = Args.getLastArg(OPT_compress_debug_sections,
+                                     OPT_compress_debug_sections_EQ)) {
+    if (A->getOption().getID() == OPT_compress_debug_sections) {
+      // TODO: be more clever about the compression type auto-detection
+      Opts.CompressDebugSections = llvm::DebugCompressionType::GNU;
+    } else {
+      Opts.CompressDebugSections =
+          llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
+              .Case("none", llvm::DebugCompressionType::None)
+              .Case("zlib", llvm::DebugCompressionType::Z)
+              .Case("zlib-gnu", llvm::DebugCompressionType::GNU)
+              .Default(llvm::DebugCompressionType::None);
+    }
+  }
+
+  Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations);
+  Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
+  Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags);
+  Opts.DwarfDebugProducer = Args.getLastArgValue(OPT_dwarf_debug_producer);
+  Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir);
+  Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name);
+
+  for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ))
+    Opts.DebugPrefixMap.insert(StringRef(Arg).split('='));
+
+  // Frontend Options
+  if (Args.hasArg(OPT_INPUT)) {
+    bool First = true;
+    for (const Arg *A : Args.filtered(OPT_INPUT)) {
+      if (First) {
+        Opts.InputFile = A->getValue();
+        First = false;
+      } else {
+        Diags.Report(diag::err_drv_unknown_argument) << A->getAsString(Args);
+        Success = false;
+      }
+    }
+  }
+  Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm);
+  Opts.OutputPath = Args.getLastArgValue(OPT_o);
+  Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file);
+  if (Arg *A = Args.getLastArg(OPT_filetype)) {
+    StringRef Name = A->getValue();
+    unsigned OutputType = StringSwitch<unsigned>(Name)
+      .Case("asm", FT_Asm)
+      .Case("null", FT_Null)
+      .Case("obj", FT_Obj)
+      .Default(~0U);
+    if (OutputType == ~0U) {
+      Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name;
+      Success = false;
+    } else
+      Opts.OutputType = FileType(OutputType);
+  }
+  Opts.ShowHelp = Args.hasArg(OPT_help);
+  Opts.ShowVersion = Args.hasArg(OPT_version);
+
+  // Transliterate Options
+  Opts.OutputAsmVariant =
+      getLastArgIntValue(Args, OPT_output_asm_variant, 0, Diags);
+  Opts.ShowEncoding = Args.hasArg(OPT_show_encoding);
+  Opts.ShowInst = Args.hasArg(OPT_show_inst);
+
+  // Assemble Options
+  Opts.RelaxAll = Args.hasArg(OPT_mrelax_all);
+  Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack);
+  Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings);
+  Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic");
+  Opts.IncrementalLinkerCompatible =
+      Args.hasArg(OPT_mincremental_linker_compatible);
+  Opts.SymbolDefs = Args.getAllArgValues(OPT_defsym);
+
+  return Success;
+}
+
+static std::unique_ptr<raw_fd_ostream>
+getOutputStream(StringRef Path, DiagnosticsEngine &Diags, bool Binary) {
+  // Make sure that the Out file gets unlinked from the disk if we get a
+  // SIGINT.
+  if (Path != "-")
+    sys::RemoveFileOnSignal(Path);
+
+  std::error_code EC;
+  auto Out = llvm::make_unique<raw_fd_ostream>(
+      Path, EC, (Binary ? sys::fs::F_None : sys::fs::F_Text));
+  if (EC) {
+    Diags.Report(diag::err_fe_unable_to_open_output) << Path << EC.message();
+    return nullptr;
+  }
+
+  return Out;
+}
+
+static bool ExecuteAssembler(AssemblerInvocation &Opts,
+                             DiagnosticsEngine &Diags) {
+  // Get the target specific parser.
+  std::string Error;
+  const Target *TheTarget = TargetRegistry::lookupTarget(Opts.Triple, Error);
+  if (!TheTarget)
+    return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
+
+  ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
+      MemoryBuffer::getFileOrSTDIN(Opts.InputFile);
+
+  if (std::error_code EC = Buffer.getError()) {
+    Error = EC.message();
+    return Diags.Report(diag::err_fe_error_reading) << Opts.InputFile;
+  }
+
+  SourceMgr SrcMgr;
+
+  // Tell SrcMgr about this buffer, which is what the parser will pick up.
+  SrcMgr.AddNewSourceBuffer(std::move(*Buffer), SMLoc());
+
+  // Record the location of the include directories so that the lexer can find
+  // it later.
+  SrcMgr.setIncludeDirs(Opts.IncludePaths);
+
+  std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(Opts.Triple));
+  assert(MRI && "Unable to create target register info!");
+
+  std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, Opts.Triple));
+  assert(MAI && "Unable to create target asm info!");
+
+  // Ensure MCAsmInfo initialization occurs before any use, otherwise sections
+  // may be created with a combination of default and explicit settings.
+  MAI->setCompressDebugSections(Opts.CompressDebugSections);
+
+  MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations);
+
+  bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj;
+  if (Opts.OutputPath.empty())
+    Opts.OutputPath = "-";
+  std::unique_ptr<raw_fd_ostream> FDOS =
+      getOutputStream(Opts.OutputPath, Diags, IsBinary);
+  if (!FDOS)
+    return true;
+  std::unique_ptr<raw_fd_ostream> DwoOS;
+  if (!Opts.SplitDwarfFile.empty())
+    DwoOS = getOutputStream(Opts.SplitDwarfFile, Diags, IsBinary);
+
+  // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and
+  // MCObjectFileInfo needs a MCContext reference in order to initialize itself.
+  std::unique_ptr<MCObjectFileInfo> MOFI(new MCObjectFileInfo());
+
+  MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr);
+
+  bool PIC = false;
+  if (Opts.RelocationModel == "static") {
+    PIC = false;
+  } else if (Opts.RelocationModel == "pic") {
+    PIC = true;
+  } else {
+    assert(Opts.RelocationModel == "dynamic-no-pic" &&
+           "Invalid PIC model!");
+    PIC = false;
+  }
+
+  MOFI->InitMCObjectFileInfo(Triple(Opts.Triple), PIC, Ctx);
+  if (Opts.SaveTemporaryLabels)
+    Ctx.setAllowTemporaryLabels(false);
+  if (Opts.GenDwarfForAssembly)
+    Ctx.setGenDwarfForAssembly(true);
+  if (!Opts.DwarfDebugFlags.empty())
+    Ctx.setDwarfDebugFlags(StringRef(Opts.DwarfDebugFlags));
+  if (!Opts.DwarfDebugProducer.empty())
+    Ctx.setDwarfDebugProducer(StringRef(Opts.DwarfDebugProducer));
+  if (!Opts.DebugCompilationDir.empty())
+    Ctx.setCompilationDir(Opts.DebugCompilationDir);
+  if (!Opts.DebugPrefixMap.empty())
+    for (const auto &KV : Opts.DebugPrefixMap)
+      Ctx.addDebugPrefixMapEntry(KV.first, KV.second);
+  if (!Opts.MainFileName.empty())
+    Ctx.setMainFileName(StringRef(Opts.MainFileName));
+  Ctx.setDwarfVersion(Opts.DwarfVersion);
+
+  // Build up the feature string from the target feature list.
+  std::string FS;
+  if (!Opts.Features.empty()) {
+    FS = Opts.Features[0];
+    for (unsigned i = 1, e = Opts.Features.size(); i != e; ++i)
+      FS += "," + Opts.Features[i];
+  }
+
+  std::unique_ptr<MCStreamer> Str;
+
+  std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
+  std::unique_ptr<MCSubtargetInfo> STI(
+      TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS));
+
+  raw_pwrite_stream *Out = FDOS.get();
+  std::unique_ptr<buffer_ostream> BOS;
+
+  // FIXME: There is a bit of code duplication with addPassesToEmitFile.
+  if (Opts.OutputType == AssemblerInvocation::FT_Asm) {
+    MCInstPrinter *IP = TheTarget->createMCInstPrinter(
+        llvm::Triple(Opts.Triple), Opts.OutputAsmVariant, *MAI, *MCII, *MRI);
+
+    std::unique_ptr<MCCodeEmitter> CE;
+    if (Opts.ShowEncoding)
+      CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
+    MCTargetOptions MCOptions;
+    std::unique_ptr<MCAsmBackend> MAB(
+        TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
+
+    auto FOut = llvm::make_unique<formatted_raw_ostream>(*Out);
+    Str.reset(TheTarget->createAsmStreamer(
+        Ctx, std::move(FOut), /*asmverbose*/ true,
+        /*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB),
+        Opts.ShowInst));
+  } else if (Opts.OutputType == AssemblerInvocation::FT_Null) {
+    Str.reset(createNullStreamer(Ctx));
+  } else {
+    assert(Opts.OutputType == AssemblerInvocation::FT_Obj &&
+           "Invalid file type!");
+    if (!FDOS->supportsSeeking()) {
+      BOS = make_unique<buffer_ostream>(*FDOS);
+      Out = BOS.get();
+    }
+
+    std::unique_ptr<MCCodeEmitter> CE(
+        TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
+    MCTargetOptions MCOptions;
+    std::unique_ptr<MCAsmBackend> MAB(
+        TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
+    std::unique_ptr<MCObjectWriter> OW =
+        DwoOS ? MAB->createDwoObjectWriter(*Out, *DwoOS)
+              : MAB->createObjectWriter(*Out);
+
+    Triple T(Opts.Triple);
+    Str.reset(TheTarget->createMCObjectStreamer(
+        T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI,
+        Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
+        /*DWARFMustBeAtTheEnd*/ true));
+    Str.get()->InitSections(Opts.NoExecStack);
+  }
+
+  // Assembly to object compilation should leverage assembly info.
+  Str->setUseAssemblerInfoForParsing(true);
+
+  bool Failed = false;
+
+  std::unique_ptr<MCAsmParser> Parser(
+      createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI));
+
+  // FIXME: init MCTargetOptions from sanitizer flags here.
+  MCTargetOptions Options;
+  std::unique_ptr<MCTargetAsmParser> TAP(
+      TheTarget->createMCAsmParser(*STI, *Parser, *MCII, Options));
+  if (!TAP)
+    Failed = Diags.Report(diag::err_target_unknown_triple) << Opts.Triple;
+
+  // Set values for symbols, if any.
+  for (auto &S : Opts.SymbolDefs) {
+    auto Pair = StringRef(S).split('=');
+    auto Sym = Pair.first;
+    auto Val = Pair.second;
+    int64_t Value;
+    // We have already error checked this in the driver.
+    Val.getAsInteger(0, Value);
+    Ctx.setSymbolValue(Parser->getStreamer(), Sym, Value);
+  }
+
+  if (!Failed) {
+    Parser->setTargetParser(*TAP.get());
+    Failed = Parser->Run(Opts.NoInitialTextSection);
+  }
+
+  // Close Streamer first.
+  // It might have a reference to the output stream.
+  Str.reset();
+  // Close the output stream early.
+  BOS.reset();
+  FDOS.reset();
+
+  // Delete output file if there were errors.
+  if (Failed) {
+    if (Opts.OutputPath != "-")
+      sys::fs::remove(Opts.OutputPath);
+    if (!Opts.SplitDwarfFile.empty() && Opts.SplitDwarfFile != "-")
+      sys::fs::remove(Opts.SplitDwarfFile);
+  }
+
+  return Failed;
+}
+
+static void LLVMErrorHandler(void *UserData, const std::string &Message,
+                             bool GenCrashDiag) {
+  DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData);
+
+  Diags.Report(diag::err_fe_error_backend) << Message;
+
+  // We cannot recover from llvm errors.
+  exit(1);
+}
+
+int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
+  // Initialize targets and assembly printers/parsers.
+  InitializeAllTargetInfos();
+  InitializeAllTargetMCs();
+  InitializeAllAsmParsers();
+
+  // Construct our diagnostic client.
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+  TextDiagnosticPrinter *DiagClient
+    = new TextDiagnosticPrinter(errs(), &*DiagOpts);
+  DiagClient->setPrefix("clang -cc1as");
+  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+  DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
+
+  // Set an error handler, so that any LLVM backend diagnostics go through our
+  // error handler.
+  ScopedFatalErrorHandler FatalErrorHandler
+    (LLVMErrorHandler, static_cast<void*>(&Diags));
+
+  // Parse the arguments.
+  AssemblerInvocation Asm;
+  if (!AssemblerInvocation::CreateFromArgs(Asm, Argv, Diags))
+    return 1;
+
+  if (Asm.ShowHelp) {
+    std::unique_ptr<OptTable> Opts(driver::createDriverOptTable());
+    Opts->PrintHelp(llvm::outs(), "clang -cc1as", "Clang Integrated Assembler",
+                    /*Include=*/driver::options::CC1AsOption, /*Exclude=*/0,
+                    /*ShowAllAliases=*/false);
+    return 0;
+  }
+
+  // Honor -version.
+  //
+  // FIXME: Use a better -version message?
+  if (Asm.ShowVersion) {
+    llvm::cl::PrintVersionMessage();
+    return 0;
+  }
+
+  // Honor -mllvm.
+  //
+  // FIXME: Remove this, one day.
+  if (!Asm.LLVMArgs.empty()) {
+    unsigned NumArgs = Asm.LLVMArgs.size();
+    auto Args = llvm::make_unique<const char*[]>(NumArgs + 2);
+    Args[0] = "clang (LLVM option parsing)";
+    for (unsigned i = 0; i != NumArgs; ++i)
+      Args[i + 1] = Asm.LLVMArgs[i].c_str();
+    Args[NumArgs + 1] = nullptr;
+    llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get());
+  }
+
+  // Execute the invocation, unless there were parsing errors.
+  bool Failed = Diags.hasErrorOccurred() || ExecuteAssembler(Asm, Diags);
+
+  // If any timers were active but haven't been destroyed yet, print their
+  // results now.
+  TimerGroup::printAll(errs());
+
+  return !!Failed;
+}
+
src/zig_clang_driver.cpp
@@ -0,0 +1,512 @@
+//===-- driver.cpp - Clang GCC-Compatible Driver --------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This is the entry point to the clang driver; it is a thin wrapper
+// for functionality in the Driver clang library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Driver/Driver.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/Driver/Options.h"
+#include "clang/Driver/ToolChain.h"
+#include "clang/Frontend/ChainedDiagnosticConsumer.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/SerializedDiagnosticPrinter.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Frontend/Utils.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/StringSaver.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+#include <set>
+#include <system_error>
+using namespace clang;
+using namespace clang::driver;
+using namespace llvm::opt;
+
+std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) {
+  if (!CanonicalPrefixes) {
+    SmallString<128> ExecutablePath(Argv0);
+    // Do a PATH lookup if Argv0 isn't a valid path.
+    if (!llvm::sys::fs::exists(ExecutablePath))
+      if (llvm::ErrorOr<std::string> P =
+              llvm::sys::findProgramByName(ExecutablePath))
+        ExecutablePath = *P;
+    return ExecutablePath.str();
+  }
+
+  // This just needs to be some symbol in the binary; C++ doesn't
+  // allow taking the address of ::main however.
+  void *P = (void*) (intptr_t) GetExecutablePath;
+  return llvm::sys::fs::getMainExecutable(Argv0, P);
+}
+
+static const char *GetStableCStr(std::set<std::string> &SavedStrings,
+                                 StringRef S) {
+  return SavedStrings.insert(S).first->c_str();
+}
+
+/// ApplyQAOverride - Apply a list of edits to the input argument lists.
+///
+/// The input string is a space separate list of edits to perform,
+/// they are applied in order to the input argument lists. Edits
+/// should be one of the following forms:
+///
+///  '#': Silence information about the changes to the command line arguments.
+///
+///  '^': Add FOO as a new argument at the beginning of the command line.
+///
+///  '+': Add FOO as a new argument at the end of the command line.
+///
+///  's/XXX/YYY/': Substitute the regular expression XXX with YYY in the command
+///  line.
+///
+///  'xOPTION': Removes all instances of the literal argument OPTION.
+///
+///  'XOPTION': Removes all instances of the literal argument OPTION,
+///  and the following argument.
+///
+///  'Ox': Removes all flags matching 'O' or 'O[sz0-9]' and adds 'Ox'
+///  at the end of the command line.
+///
+/// \param OS - The stream to write edit information to.
+/// \param Args - The vector of command line arguments.
+/// \param Edit - The override command to perform.
+/// \param SavedStrings - Set to use for storing string representations.
+static void ApplyOneQAOverride(raw_ostream &OS,
+                               SmallVectorImpl<const char*> &Args,
+                               StringRef Edit,
+                               std::set<std::string> &SavedStrings) {
+  // This does not need to be efficient.
+
+  if (Edit[0] == '^') {
+    const char *Str =
+      GetStableCStr(SavedStrings, Edit.substr(1));
+    OS << "### Adding argument " << Str << " at beginning\n";
+    Args.insert(Args.begin() + 1, Str);
+  } else if (Edit[0] == '+') {
+    const char *Str =
+      GetStableCStr(SavedStrings, Edit.substr(1));
+    OS << "### Adding argument " << Str << " at end\n";
+    Args.push_back(Str);
+  } else if (Edit[0] == 's' && Edit[1] == '/' && Edit.endswith("/") &&
+             Edit.slice(2, Edit.size()-1).find('/') != StringRef::npos) {
+    StringRef MatchPattern = Edit.substr(2).split('/').first;
+    StringRef ReplPattern = Edit.substr(2).split('/').second;
+    ReplPattern = ReplPattern.slice(0, ReplPattern.size()-1);
+
+    for (unsigned i = 1, e = Args.size(); i != e; ++i) {
+      // Ignore end-of-line response file markers
+      if (Args[i] == nullptr)
+        continue;
+      std::string Repl = llvm::Regex(MatchPattern).sub(ReplPattern, Args[i]);
+
+      if (Repl != Args[i]) {
+        OS << "### Replacing '" << Args[i] << "' with '" << Repl << "'\n";
+        Args[i] = GetStableCStr(SavedStrings, Repl);
+      }
+    }
+  } else if (Edit[0] == 'x' || Edit[0] == 'X') {
+    auto Option = Edit.substr(1);
+    for (unsigned i = 1; i < Args.size();) {
+      if (Option == Args[i]) {
+        OS << "### Deleting argument " << Args[i] << '\n';
+        Args.erase(Args.begin() + i);
+        if (Edit[0] == 'X') {
+          if (i < Args.size()) {
+            OS << "### Deleting argument " << Args[i] << '\n';
+            Args.erase(Args.begin() + i);
+          } else
+            OS << "### Invalid X edit, end of command line!\n";
+        }
+      } else
+        ++i;
+    }
+  } else if (Edit[0] == 'O') {
+    for (unsigned i = 1; i < Args.size();) {
+      const char *A = Args[i];
+      // Ignore end-of-line response file markers
+      if (A == nullptr)
+        continue;
+      if (A[0] == '-' && A[1] == 'O' &&
+          (A[2] == '\0' ||
+           (A[3] == '\0' && (A[2] == 's' || A[2] == 'z' ||
+                             ('0' <= A[2] && A[2] <= '9'))))) {
+        OS << "### Deleting argument " << Args[i] << '\n';
+        Args.erase(Args.begin() + i);
+      } else
+        ++i;
+    }
+    OS << "### Adding argument " << Edit << " at end\n";
+    Args.push_back(GetStableCStr(SavedStrings, '-' + Edit.str()));
+  } else {
+    OS << "### Unrecognized edit: " << Edit << "\n";
+  }
+}
+
+/// ApplyQAOverride - Apply a comma separate list of edits to the
+/// input argument lists. See ApplyOneQAOverride.
+static void ApplyQAOverride(SmallVectorImpl<const char*> &Args,
+                            const char *OverrideStr,
+                            std::set<std::string> &SavedStrings) {
+  raw_ostream *OS = &llvm::errs();
+
+  if (OverrideStr[0] == '#') {
+    ++OverrideStr;
+    OS = &llvm::nulls();
+  }
+
+  *OS << "### CCC_OVERRIDE_OPTIONS: " << OverrideStr << "\n";
+
+  // This does not need to be efficient.
+
+  const char *S = OverrideStr;
+  while (*S) {
+    const char *End = ::strchr(S, ' ');
+    if (!End)
+      End = S + strlen(S);
+    if (End != S)
+      ApplyOneQAOverride(*OS, Args, std::string(S, End), SavedStrings);
+    S = End;
+    if (*S != '\0')
+      ++S;
+  }
+}
+
+extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0,
+                    void *MainAddr);
+extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
+                      void *MainAddr);
+
+static void insertTargetAndModeArgs(const ParsedClangName &NameParts,
+                                    SmallVectorImpl<const char *> &ArgVector,
+                                    std::set<std::string> &SavedStrings) {
+  // Put target and mode arguments at the start of argument list so that
+  // arguments specified in command line could override them. Avoid putting
+  // them at index 0, as an option like '-cc1' must remain the first.
+  int InsertionPoint = 0;
+  if (ArgVector.size() > 0)
+    ++InsertionPoint;
+
+  if (NameParts.DriverMode) {
+    // Add the mode flag to the arguments.
+    ArgVector.insert(ArgVector.begin() + InsertionPoint,
+                     GetStableCStr(SavedStrings, NameParts.DriverMode));
+  }
+
+  if (NameParts.TargetIsValid) {
+    const char *arr[] = {"-target", GetStableCStr(SavedStrings,
+                                                  NameParts.TargetPrefix)};
+    ArgVector.insert(ArgVector.begin() + InsertionPoint,
+                     std::begin(arr), std::end(arr));
+  }
+}
+
+static void getCLEnvVarOptions(std::string &EnvValue, llvm::StringSaver &Saver,
+                               SmallVectorImpl<const char *> &Opts) {
+  llvm::cl::TokenizeWindowsCommandLine(EnvValue, Saver, Opts);
+  // The first instance of '#' should be replaced with '=' in each option.
+  for (const char *Opt : Opts)
+    if (char *NumberSignPtr = const_cast<char *>(::strchr(Opt, '#')))
+      *NumberSignPtr = '=';
+}
+
+static void SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) {
+  // Handle CC_PRINT_OPTIONS and CC_PRINT_OPTIONS_FILE.
+  TheDriver.CCPrintOptions = !!::getenv("CC_PRINT_OPTIONS");
+  if (TheDriver.CCPrintOptions)
+    TheDriver.CCPrintOptionsFilename = ::getenv("CC_PRINT_OPTIONS_FILE");
+
+  // Handle CC_PRINT_HEADERS and CC_PRINT_HEADERS_FILE.
+  TheDriver.CCPrintHeaders = !!::getenv("CC_PRINT_HEADERS");
+  if (TheDriver.CCPrintHeaders)
+    TheDriver.CCPrintHeadersFilename = ::getenv("CC_PRINT_HEADERS_FILE");
+
+  // Handle CC_LOG_DIAGNOSTICS and CC_LOG_DIAGNOSTICS_FILE.
+  TheDriver.CCLogDiagnostics = !!::getenv("CC_LOG_DIAGNOSTICS");
+  if (TheDriver.CCLogDiagnostics)
+    TheDriver.CCLogDiagnosticsFilename = ::getenv("CC_LOG_DIAGNOSTICS_FILE");
+}
+
+static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient,
+                                   const std::string &Path) {
+  // If the clang binary happens to be named cl.exe for compatibility reasons,
+  // use clang-cl.exe as the prefix to avoid confusion between clang and MSVC.
+  StringRef ExeBasename(llvm::sys::path::filename(Path));
+  if (ExeBasename.equals_lower("cl.exe"))
+    ExeBasename = "clang-cl.exe";
+  DiagClient->setPrefix(ExeBasename);
+}
+
+// This lets us create the DiagnosticsEngine with a properly-filled-out
+// DiagnosticOptions instance.
+static DiagnosticOptions *
+CreateAndPopulateDiagOpts(ArrayRef<const char *> argv) {
+  auto *DiagOpts = new DiagnosticOptions;
+  std::unique_ptr<OptTable> Opts(createDriverOptTable());
+  unsigned MissingArgIndex, MissingArgCount;
+  InputArgList Args =
+      Opts->ParseArgs(argv.slice(1), MissingArgIndex, MissingArgCount);
+  // We ignore MissingArgCount and the return value of ParseDiagnosticArgs.
+  // Any errors that would be diagnosed here will also be diagnosed later,
+  // when the DiagnosticsEngine actually exists.
+  (void)ParseDiagnosticArgs(*DiagOpts, Args);
+  return DiagOpts;
+}
+
+static void SetInstallDir(SmallVectorImpl<const char *> &argv,
+                          Driver &TheDriver, bool CanonicalPrefixes) {
+  // Attempt to find the original path used to invoke the driver, to determine
+  // the installed path. We do this manually, because we want to support that
+  // path being a symlink.
+  SmallString<128> InstalledPath(argv[0]);
+
+  // Do a PATH lookup, if there are no directory components.
+  if (llvm::sys::path::filename(InstalledPath) == InstalledPath)
+    if (llvm::ErrorOr<std::string> Tmp = llvm::sys::findProgramByName(
+            llvm::sys::path::filename(InstalledPath.str())))
+      InstalledPath = *Tmp;
+
+  // FIXME: We don't actually canonicalize this, we just make it absolute.
+  if (CanonicalPrefixes)
+    llvm::sys::fs::make_absolute(InstalledPath);
+
+  StringRef InstalledPathParent(llvm::sys::path::parent_path(InstalledPath));
+  if (llvm::sys::fs::exists(InstalledPathParent))
+    TheDriver.setInstalledDir(InstalledPathParent);
+}
+
+static int ExecuteCC1Tool(ArrayRef<const char *> argv, StringRef Tool) {
+  void *GetExecutablePathVP = (void *)(intptr_t) GetExecutablePath;
+  if (Tool == "")
+    return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP);
+  if (Tool == "as")
+    return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP);
+
+  // Reject unknown tools.
+  llvm::errs() << "error: unknown integrated tool '" << Tool << "'. "
+               << "Valid tools include '-cc1' and '-cc1as'.\n";
+  return 1;
+}
+
+extern "C" int ZigClang_main(int argc_, const char **argv_);
+int ZigClang_main(int argc_, const char **argv_) {
+  llvm::InitLLVM X(argc_, argv_);
+  SmallVector<const char *, 256> argv(argv_, argv_ + argc_);
+
+  if (llvm::sys::Process::FixupStandardFileDescriptors())
+    return 1;
+
+  llvm::InitializeAllTargets();
+  auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(argv[0]);
+
+  llvm::BumpPtrAllocator A;
+  llvm::StringSaver Saver(A);
+
+  // Parse response files using the GNU syntax, unless we're in CL mode. There
+  // are two ways to put clang in CL compatibility mode: argv[0] is either
+  // clang-cl or cl, or --driver-mode=cl is on the command line. The normal
+  // command line parsing can't happen until after response file parsing, so we
+  // have to manually search for a --driver-mode=cl argument the hard way.
+  // Finally, our -cc1 tools don't care which tokenization mode we use because
+  // response files written by clang will tokenize the same way in either mode.
+  bool ClangCLMode = false;
+  if (StringRef(TargetAndMode.DriverMode).equals("--driver-mode=cl") ||
+      std::find_if(argv.begin(), argv.end(), [](const char *F) {
+        return F && strcmp(F, "--driver-mode=cl") == 0;
+      }) != argv.end()) {
+    ClangCLMode = true;
+  }
+  enum { Default, POSIX, Windows } RSPQuoting = Default;
+  for (const char *F : argv) {
+    if (strcmp(F, "--rsp-quoting=posix") == 0)
+      RSPQuoting = POSIX;
+    else if (strcmp(F, "--rsp-quoting=windows") == 0)
+      RSPQuoting = Windows;
+  }
+
+  // Determines whether we want nullptr markers in argv to indicate response
+  // files end-of-lines. We only use this for the /LINK driver argument with
+  // clang-cl.exe on Windows.
+  bool MarkEOLs = ClangCLMode;
+
+  llvm::cl::TokenizerCallback Tokenizer;
+  if (RSPQuoting == Windows || (RSPQuoting == Default && ClangCLMode))
+    Tokenizer = &llvm::cl::TokenizeWindowsCommandLine;
+  else
+    Tokenizer = &llvm::cl::TokenizeGNUCommandLine;
+
+  if (MarkEOLs && argv.size() > 1 && StringRef(argv[1]).startswith("-cc1"))
+    MarkEOLs = false;
+  llvm::cl::ExpandResponseFiles(Saver, Tokenizer, argv, MarkEOLs);
+
+  // Handle -cc1 integrated tools, even if -cc1 was expanded from a response
+  // file.
+  auto FirstArg = std::find_if(argv.begin() + 1, argv.end(),
+                               [](const char *A) { return A != nullptr; });
+  if (FirstArg != argv.end() && StringRef(*FirstArg).startswith("-cc1")) {
+    // If -cc1 came from a response file, remove the EOL sentinels.
+    if (MarkEOLs) {
+      auto newEnd = std::remove(argv.begin(), argv.end(), nullptr);
+      argv.resize(newEnd - argv.begin());
+    }
+    return ExecuteCC1Tool(argv, argv[1] + 4);
+  }
+
+  bool CanonicalPrefixes = true;
+  for (int i = 1, size = argv.size(); i < size; ++i) {
+    // Skip end-of-line response file markers
+    if (argv[i] == nullptr)
+      continue;
+    if (StringRef(argv[i]) == "-no-canonical-prefixes") {
+      CanonicalPrefixes = false;
+      break;
+    }
+  }
+
+  // Handle CL and _CL_ which permits additional command line options to be
+  // prepended or appended.
+  if (ClangCLMode) {
+    // Arguments in "CL" are prepended.
+    llvm::Optional<std::string> OptCL = llvm::sys::Process::GetEnv("CL");
+    if (OptCL.hasValue()) {
+      SmallVector<const char *, 8> PrependedOpts;
+      getCLEnvVarOptions(OptCL.getValue(), Saver, PrependedOpts);
+
+      // Insert right after the program name to prepend to the argument list.
+      argv.insert(argv.begin() + 1, PrependedOpts.begin(), PrependedOpts.end());
+    }
+    // Arguments in "_CL_" are appended.
+    llvm::Optional<std::string> Opt_CL_ = llvm::sys::Process::GetEnv("_CL_");
+    if (Opt_CL_.hasValue()) {
+      SmallVector<const char *, 8> AppendedOpts;
+      getCLEnvVarOptions(Opt_CL_.getValue(), Saver, AppendedOpts);
+
+      // Insert at the end of the argument list to append.
+      argv.append(AppendedOpts.begin(), AppendedOpts.end());
+    }
+  }
+
+  std::set<std::string> SavedStrings;
+  // Handle CCC_OVERRIDE_OPTIONS, used for editing a command line behind the
+  // scenes.
+  if (const char *OverrideStr = ::getenv("CCC_OVERRIDE_OPTIONS")) {
+    // FIXME: Driver shouldn't take extra initial argument.
+    ApplyQAOverride(argv, OverrideStr, SavedStrings);
+  }
+
+  std::string Path = GetExecutablePath(argv[0], CanonicalPrefixes);
+
+  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
+      CreateAndPopulateDiagOpts(argv);
+
+  TextDiagnosticPrinter *DiagClient
+    = new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts);
+  FixupDiagPrefixExeName(DiagClient, Path);
+
+  IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
+
+  DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
+
+  if (!DiagOpts->DiagnosticSerializationFile.empty()) {
+    auto SerializedConsumer =
+        clang::serialized_diags::create(DiagOpts->DiagnosticSerializationFile,
+                                        &*DiagOpts, /*MergeChildRecords=*/true);
+    Diags.setClient(new ChainedDiagnosticConsumer(
+        Diags.takeClient(), std::move(SerializedConsumer)));
+  }
+
+  ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
+
+  Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags);
+  SetInstallDir(argv, TheDriver, CanonicalPrefixes);
+  TheDriver.setTargetAndMode(TargetAndMode);
+
+  insertTargetAndModeArgs(TargetAndMode, argv, SavedStrings);
+
+  SetBackdoorDriverOutputsFromEnvVars(TheDriver);
+
+  std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(argv));
+  int Res = 1;
+  if (C && !C->containsError()) {
+    SmallVector<std::pair<int, const Command *>, 4> FailingCommands;
+    Res = TheDriver.ExecuteCompilation(*C, FailingCommands);
+
+    // Force a crash to test the diagnostics.
+    if (TheDriver.GenReproducer) {
+      Diags.Report(diag::err_drv_force_crash)
+        << !::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH");
+
+      // Pretend that every command failed.
+      FailingCommands.clear();
+      for (const auto &J : C->getJobs())
+        if (const Command *C = dyn_cast<Command>(&J))
+          FailingCommands.push_back(std::make_pair(-1, C));
+    }
+
+    for (const auto &P : FailingCommands) {
+      int CommandRes = P.first;
+      const Command *FailingCommand = P.second;
+      if (!Res)
+        Res = CommandRes;
+
+      // If result status is < 0, then the driver command signalled an error.
+      // If result status is 70, then the driver command reported a fatal error.
+      // On Windows, abort will return an exit code of 3.  In these cases,
+      // generate additional diagnostic information if possible.
+      bool DiagnoseCrash = CommandRes < 0 || CommandRes == 70;
+#ifdef _WIN32
+      DiagnoseCrash |= CommandRes == 3;
+#endif
+      if (DiagnoseCrash) {
+        TheDriver.generateCompilationDiagnostics(*C, *FailingCommand);
+        break;
+      }
+    }
+  }
+
+  Diags.getClient()->finish();
+
+  // If any timers were active but haven't been destroyed yet, print their
+  // results now.  This happens in -disable-free mode.
+  llvm::TimerGroup::printAll(llvm::errs());
+
+#ifdef _WIN32
+  // Exit status should not be negative on Win32, unless abnormal termination.
+  // Once abnormal termiation was caught, negative status should not be
+  // propagated.
+  if (Res < 0)
+    Res = 1;
+#endif
+
+  // If we have multiple failing commands, we return the result of the first
+  // failing command.
+  return Res;
+}
+
CMakeLists.txt
@@ -431,6 +431,9 @@ set(BLAKE_SOURCES
 set(ZIG_CPP_SOURCES
     "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
     "${CMAKE_SOURCE_DIR}/src/zig_clang.cpp"
+    "${CMAKE_SOURCE_DIR}/src/zig_clang_driver.cpp"
+    "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1_main.cpp"
+    "${CMAKE_SOURCE_DIR}/src/zig_clang_cc1as_main.cpp"
     "${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp"
 )