//===--- ToolChains.cpp - Job invocations (general and per-platform) ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "ToolChains.h"

#include "swift/AST/DiagnosticsDriver.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/Platform.h"
#include "swift/Basic/Range.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Basic/TaskQueue.h"
#include "swift/Config.h"
#include "swift/Driver/Compilation.h"
#include "swift/Driver/Driver.h"
#include "swift/Driver/Job.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Option/Options.h"
#include "clang/Basic/Version.h"
#include "clang/Driver/Util.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"

using namespace swift;
using namespace swift::driver;
using namespace llvm::opt;

bool ToolChain::JobContext::shouldUseInputFileList() const {
  return getTopLevelInputFiles().size() > C.getFilelistThreshold();
}

bool ToolChain::JobContext::shouldUsePrimaryInputFileListInFrontendInvocation()
    const {
  return InputActions.size() > C.getFilelistThreshold();
}

bool ToolChain::JobContext::shouldUseMainOutputFileListInFrontendInvocation()
    const {
  return Output.getPrimaryOutputFilenames().size() > C.getFilelistThreshold();
}

bool ToolChain::JobContext::
    shouldUseSupplementaryOutputFileMapInFrontendInvocation() const {
  static const unsigned UpperBoundOnSupplementaryOutputFileTypes =
      file_types::TY_INVALID;
  return InputActions.size() * UpperBoundOnSupplementaryOutputFileTypes >
         C.getFilelistThreshold();
}

bool ToolChain::JobContext::shouldFilterFrontendInputsByType() const {
  // FIXME: SingleCompile has not filtered its inputs in the past and now people
  // rely upon that. But we would like the compilation modes to be consistent.
  return OI.CompilerMode != OutputInfo::Mode::SingleCompile;
}

void ToolChain::addInputsOfType(ArgStringList &Arguments,
                                ArrayRef<const Action *> Inputs,
                                file_types::ID InputType,
                                const char *PrefixArgument) const {
  for (auto &Input : Inputs) {
    if (Input->getType() != InputType)
      continue;
    if (PrefixArgument)
      Arguments.push_back(PrefixArgument);
    Arguments.push_back(cast<InputAction>(Input)->getInputArg().getValue());
  }
}

void ToolChain::addInputsOfType(ArgStringList &Arguments,
                                ArrayRef<const Job *> Jobs,
                                const llvm::opt::ArgList &Args,
                                file_types::ID InputType,
                                const char *PrefixArgument) const {
  for (const Job *Cmd : Jobs) {
    auto output = Cmd->getOutput().getAnyOutputForType(InputType);
    if (!output.empty()) {
      if (PrefixArgument)
        Arguments.push_back(PrefixArgument);
      Arguments.push_back(Args.MakeArgString(output));
    }
  }
}

void ToolChain::addPrimaryInputsOfType(ArgStringList &Arguments,
                                       ArrayRef<const Job *> Jobs,
                                       const llvm::opt::ArgList &Args,
                                       file_types::ID InputType,
                                       const char *PrefixArgument) const {
  for (const Job *Cmd : Jobs) {
    auto &outputInfo = Cmd->getOutput();
    if (outputInfo.getPrimaryOutputType() == InputType) {
      for (auto Output : outputInfo.getPrimaryOutputFilenames()) {
        if (PrefixArgument)
          Arguments.push_back(PrefixArgument);
        Arguments.push_back(Args.MakeArgString(Output));
      }
    }
  }
}

static bool addOutputsOfType(ArgStringList &Arguments,
                             CommandOutput const &Output,
                             const llvm::opt::ArgList &Args,
                             file_types::ID OutputType,
                             const char *PrefixArgument = nullptr) {
  bool Added = false;
  for (auto Output : Output.getAdditionalOutputsForType(OutputType)) {
    assert(!Output.empty());
    if (PrefixArgument)
      Arguments.push_back(PrefixArgument);
    Arguments.push_back(Args.MakeArgString(Output));
    Added = true;
  }
  return Added;
}

static void addLTOArgs(const OutputInfo &OI, ArgStringList &arguments) {
  switch (OI.LTOVariant) {
  case OutputInfo::LTOKind::None:
    break;
  case OutputInfo::LTOKind::LLVMThin:
    arguments.push_back("-lto=llvm-thin");
    break;
  case OutputInfo::LTOKind::LLVMFull:
    arguments.push_back("-lto=llvm-full");
    break;
  }
}

namespace {

template<typename Container>
bool containsValue(
    const Container &container,
    typename Container::value_type const &element
) {
  for (const auto &value : container) {
    if (value == element)
      return true;
  }

  return false;
}

}

void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
                                      const CommandOutput &output,
                                      const ArgList &inputArgs,
                                      ArgStringList &arguments) const {
  // Only pass -target to the REPL or immediate modes if it was explicitly
  // specified on the command line.
  switch (OI.CompilerMode) {
  case OutputInfo::Mode::REPL:
  case OutputInfo::Mode::Immediate:
    if (!inputArgs.hasArg(options::OPT_target))
      break;
    LLVM_FALLTHROUGH;
  case OutputInfo::Mode::StandardCompile:
  case OutputInfo::Mode::SingleCompile:
    arguments.push_back("-target");
    arguments.push_back(inputArgs.MakeArgString(Triple.str()));
    break;
  }

  if (const Arg *variant = inputArgs.getLastArg(options::OPT_target_variant)) {
    arguments.push_back("-target-variant");
    std::string normalized = llvm::Triple::normalize(variant->getValue());
    arguments.push_back(inputArgs.MakeArgString(normalized));
  }

  // Enable address top-byte ignored in the ARM64 backend.
  if (Triple.getArch() == llvm::Triple::aarch64 ||
      Triple.getArch() == llvm::Triple::aarch64_32) {
    arguments.push_back("-Xllvm");
    arguments.push_back("-aarch64-use-tbi");
  }

  // Enable or disable ObjC interop appropriately for the platform
  if (Triple.isOSDarwin() &&
      !containsValue(
          inputArgs
            .getAllArgValues(options::OPT_enable_experimental_feature),
          "Embedded")) {
    arguments.push_back("-enable-objc-interop");
  } else {
    arguments.push_back("-disable-objc-interop");
  }

  if (Triple.isOSOpenBSD() && Triple.getArch() == llvm::Triple::aarch64) {
#ifdef SWIFT_OPENBSD_BTCFI
    arguments.push_back("-Xcc");
    arguments.push_back("-Xclang=-mbranch-target-enforce");
    arguments.push_back("-Xcc");
    arguments.push_back("-Xclang=-msign-return-address=non-leaf");
    arguments.push_back("-Xcc");
    arguments.push_back("-Xclang=-msign-return-address-key=a_key");
#endif
  }

  if (inputArgs.getLastArg(options::OPT_experimental_serialize_debug_info)) {
    arguments.push_back(
        inputArgs.MakeArgString(Twine("-experimental-serialize-debug-info")));
  }

  if (inputArgs.hasArg(options::OPT_experimental_hermetic_seal_at_link)) {
    arguments.push_back("-enable-llvm-vfe");
    arguments.push_back("-enable-llvm-wme");
    arguments.push_back("-conditional-runtime-records");
    arguments.push_back("-internalize-at-link");
  }

  // Handle the CPU and its preferences.
  inputArgs.AddLastArg(arguments, options::OPT_target_cpu);

  if (!OI.SDKPath.empty()) {
    arguments.push_back("-sdk");
    arguments.push_back(inputArgs.MakeArgString(OI.SDKPath));
  }

  if (const Arg *A = inputArgs.getLastArg(options::OPT_windows_sdk_root)) {
    arguments.push_back("-windows-sdk-root");
    arguments.push_back(inputArgs.MakeArgString(A->getValue()));
  }
  if (const Arg *A = inputArgs.getLastArg(options::OPT_windows_sdk_version)) {
    arguments.push_back("-windows-sdk-version");
    arguments.push_back(inputArgs.MakeArgString(A->getValue()));
  }
  if (const Arg *A = inputArgs.getLastArg(options::OPT_visualc_tools_root)) {
    arguments.push_back("-visualc-tools-root");
    arguments.push_back(inputArgs.MakeArgString(A->getValue()));
  }
  if (const Arg *A = inputArgs.getLastArg(options::OPT_visualc_tools_version)) {
    arguments.push_back("-visualc-tools-version");
    arguments.push_back(inputArgs.MakeArgString(A->getValue()));
  }

  if (const Arg *A = inputArgs.getLastArg(options::OPT_sysroot)) {
    arguments.push_back("-sysroot");
    arguments.push_back(inputArgs.MakeArgString(A->getValue()));
  }

  if (llvm::sys::Process::StandardErrHasColors()) {
    arguments.push_back("-color-diagnostics");
  }

  inputArgs.addAllArgs(arguments, {options::OPT_I, options::OPT_Isystem});
  inputArgs.addAllArgs(arguments, {options::OPT_F, options::OPT_Fsystem});
  inputArgs.AddAllArgs(arguments, options::OPT_vfsoverlay);

  inputArgs.AddLastArg(arguments, options::OPT_AssertConfig);
  inputArgs.AddLastArg(arguments, options::OPT_autolink_force_load);
  inputArgs.AddLastArg(arguments,
                       options::OPT_color_diagnostics,
                       options::OPT_no_color_diagnostics);
  inputArgs.AddLastArg(arguments, options::OPT_fixit_all);
  inputArgs.AddLastArg(arguments,
                       options::OPT_warn_swift3_objc_inference_minimal,
                       options::OPT_warn_swift3_objc_inference_complete);
  inputArgs.AddLastArg(arguments,
                       options::OPT_enable_actor_data_race_checks,
                       options::OPT_disable_actor_data_race_checks);
  inputArgs.AddLastArg(arguments, options::OPT_disable_dynamic_actor_isolation);
  inputArgs.AddLastArg(arguments, options::OPT_warn_concurrency);
  inputArgs.AddLastArg(arguments, options::OPT_strict_concurrency);
  inputArgs.addAllArgs(arguments, {options::OPT_enable_experimental_feature,
                                   options::OPT_disable_experimental_feature,
                                   options::OPT_enable_upcoming_feature,
                                   options::OPT_disable_upcoming_feature});
  inputArgs.AddLastArg(arguments, options::OPT_strict_memory_safety,
                       options::OPT_strict_memory_safety_migrate);
  inputArgs.AddLastArg(arguments, options::OPT_warn_implicit_overrides);
  inputArgs.AddLastArg(arguments, options::OPT_typo_correction_limit);
  inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension);
  inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension_library);
  inputArgs.AddLastArg(arguments, options::OPT_enable_library_evolution);
  inputArgs.AddLastArg(arguments, options::OPT_default_isolation);
  inputArgs.AddLastArg(arguments, options::OPT_default_isolation_EQ);
  inputArgs.AddLastArg(arguments, options::OPT_require_explicit_availability);
  inputArgs.AddLastArg(arguments, options::OPT_require_explicit_availability_target);
  inputArgs.AddLastArg(arguments, options::OPT_require_explicit_availability_EQ);
  inputArgs.AddLastArg(arguments, options::OPT_require_explicit_sendable);
  inputArgs.AddLastArg(arguments, options::OPT_check_api_availability_only);
  inputArgs.AddLastArg(arguments, options::OPT_enable_testing);
  inputArgs.AddLastArg(arguments, options::OPT_enable_private_imports);
  inputArgs.AddLastArg(arguments, options::OPT_g_Group);
  inputArgs.AddLastArg(arguments, options::OPT_debug_info_format);
  inputArgs.AddLastArg(arguments, options::OPT_dwarf_version);
  inputArgs.AddLastArg(arguments, options::OPT_import_underlying_module);
  inputArgs.AddLastArg(arguments, options::OPT_module_cache_path);
  inputArgs.AddLastArg(arguments, options::OPT_module_link_name);
  inputArgs.AddLastArg(arguments, options::OPT_module_abi_name);
  inputArgs.AddLastArg(arguments, options::OPT_package_name);
  inputArgs.AddLastArg(arguments, options::OPT_export_as);
  inputArgs.AddLastArg(arguments, options::OPT_nostdimport);
  inputArgs.AddLastArg(arguments, options::OPT_nostdlibimport);
  inputArgs.AddLastArg(arguments, options::OPT_parse_stdlib);
  inputArgs.AddLastArg(arguments, options::OPT_resource_dir);
  inputArgs.AddLastArg(arguments, options::OPT_value_recursion_threshold);
  inputArgs.AddLastArg(arguments, options::OPT_warn_swift3_objc_inference);
  inputArgs.AddLastArg(arguments, options::OPT_Rpass_EQ);
  inputArgs.AddLastArg(arguments, options::OPT_Rpass_missed_EQ);
  inputArgs.AddLastArg(arguments, options::OPT_suppress_warnings);
  inputArgs.AddLastArg(arguments, options::OPT_suppress_remarks);
  inputArgs.AddLastArg(arguments, options::OPT_experimental_package_bypass_resilience);
  inputArgs.AddLastArg(arguments, options::OPT_ExperimentalPackageCMO);
  inputArgs.AddLastArg(arguments, options::OPT_PackageCMO);
  inputArgs.AddLastArg(arguments, options::OPT_profile_generate);
  inputArgs.AddLastArg(arguments, options::OPT_profile_use);
  inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping);
  inputArgs.AddAllArgs(arguments, options::OPT_warning_treating_Group);
  inputArgs.AddLastArg(arguments, options::OPT_sanitize_EQ);
  inputArgs.AddLastArg(arguments, options::OPT_sanitize_recover_EQ);
  inputArgs.AddLastArg(arguments,
                       options::OPT_sanitize_address_use_odr_indicator);
  inputArgs.AddLastArg(arguments, options::OPT_sanitize_coverage_EQ);
  inputArgs.AddLastArg(arguments, options::OPT_sanitize_stable_abi_EQ);
  inputArgs.AddLastArg(arguments, options::OPT_static);
  inputArgs.AddLastArg(arguments, options::OPT_swift_version);
  inputArgs.AddLastArg(arguments, options::OPT_enforce_exclusivity_EQ);
  inputArgs.AddLastArg(arguments, options::OPT_stats_output_dir);
  inputArgs.AddLastArg(arguments, options::OPT_tools_directory);
  inputArgs.AddLastArg(arguments, options::OPT_trace_stats_events);
  inputArgs.AddLastArg(arguments, options::OPT_profile_stats_events);
  inputArgs.AddLastArg(arguments, options::OPT_profile_stats_entities);
  inputArgs.AddLastArg(arguments,
                       options::OPT_solver_shrink_unsolved_threshold);
  inputArgs.AddLastArg(arguments, options::OPT_O_Group);
  inputArgs.AddLastArg(arguments, options::OPT_RemoveRuntimeAsserts);
  inputArgs.AddLastArg(arguments, options::OPT_AssumeSingleThreaded);
  inputArgs.AddLastArg(arguments,
                       options::OPT_emit_fine_grained_dependency_sourcefile_dot_files);
  inputArgs.AddLastArg(arguments, options::OPT_package_description_version);
  inputArgs.AddLastArg(arguments, options::OPT_locale);
  inputArgs.AddLastArg(arguments, options::OPT_localization_path);
  inputArgs.AddLastArg(arguments, options::OPT_serialize_diagnostics_path);
  inputArgs.AddLastArg(arguments, options::OPT_debug_diagnostic_names);
  inputArgs.AddLastArg(arguments, options::OPT_print_educational_notes);
  inputArgs.AddLastArg(arguments, options::OPT_diagnostic_style);
  inputArgs.AddLastArg(arguments,
                       options::OPT_enable_experimental_concise_pound_file);
  inputArgs.AddLastArg(arguments, options::OPT_access_notes_path);
  inputArgs.AddLastArg(arguments, options::OPT_library_level);
  inputArgs.AddLastArg(arguments, options::OPT_enable_bare_slash_regex);
  inputArgs.AddLastArg(arguments, options::OPT_enable_experimental_cxx_interop);
  inputArgs.AddLastArg(arguments, options::OPT_cxx_interoperability_mode);
  inputArgs.AddLastArg(arguments, options::OPT_enable_builtin_module);
  inputArgs.AddLastArg(arguments, options::OPT_compiler_assertions);
  inputArgs.AddLastArg(arguments, options::OPT_load_pass_plugin_EQ);
  inputArgs.AddAllArgs(arguments, options::OPT_module_alias);
  inputArgs.AddLastArg(arguments, options::OPT_dump_ast_format);

  // Pass on any build config options
  inputArgs.AddAllArgs(arguments, options::OPT_D);

  // Pass on file paths that should be remapped in debug info.
  inputArgs.addAllArgs(arguments, {options::OPT_debug_prefix_map,
                                   options::OPT_coverage_prefix_map,
                                   options::OPT_file_prefix_map});

  std::string globalRemapping = getGlobalDebugPathRemapping();
  if (!globalRemapping.empty()) {
    arguments.push_back("-debug-prefix-map");
    arguments.push_back(inputArgs.MakeArgString(globalRemapping));
  }

  // Pass through the values passed to -Xfrontend.
  inputArgs.AddAllArgValues(arguments, options::OPT_Xfrontend);

  // Pass on module names whose symbols should be embedded in tbd.
  inputArgs.AddAllArgs(arguments, options::OPT_embed_tbd_for_module);

  if (auto *A = inputArgs.getLastArg(options::OPT_working_directory)) {
    // Add -Xcc -working-directory before any other -Xcc options to ensure it is
    // overridden by an explicit -Xcc -working-directory, although having a
    // different working directory is probably incorrect.
    SmallString<128> workingDirectory(A->getValue());
    llvm::sys::fs::make_absolute(workingDirectory);
    arguments.push_back("-Xcc");
    arguments.push_back("-working-directory");
    arguments.push_back("-Xcc");
    arguments.push_back(inputArgs.MakeArgString(workingDirectory));
  }

  addLTOArgs(OI, arguments);

  // -g implies -enable-anonymous-context-mangled-names, because the extra
  // metadata aids debugging.
  if (inputArgs.hasArg(options::OPT_g)) {
    // But don't add the option in optimized builds: it would prevent dead code
    // stripping of unused metadata.
    auto OptArg = inputArgs.getLastArgNoClaim(options::OPT_O_Group);
    if (!OptArg || OptArg->getOption().matches(options::OPT_Onone))
      arguments.push_back("-enable-anonymous-context-mangled-names");

    // TODO: Should we support -fcoverage-compilation-dir?
    inputArgs.AddAllArgs(arguments, options::OPT_file_compilation_dir);
  }

  // Specify default plugin search path options after explicitly specified
  // options.
  inputArgs.AddAllArgs(arguments, options::OPT_plugin_search_Group);
  addPlatformSpecificPluginFrontendArgs(OI, output, inputArgs, arguments);

  addPluginArguments(inputArgs, arguments);

  // Pass along -no-verify-emitted-module-interface only if it's effective.
  // Assume verification by default as we want to know only when the user skips
  // the verification.
  if (!inputArgs.hasFlag(options::OPT_verify_emitted_module_interface,
                         options::OPT_no_verify_emitted_module_interface,
                         true))
    arguments.push_back("-no-verify-emitted-module-interface");

  // Pass through any subsystem flags.
  inputArgs.AddAllArgs(arguments, options::OPT_Xllvm);
  inputArgs.AddAllArgs(arguments, options::OPT_Xcc);
}

void ToolChain::addPlatformSpecificPluginFrontendArgs(
    const OutputInfo &OI,
    const CommandOutput &output,
    const llvm::opt::ArgList &inputArgs,
    llvm::opt::ArgStringList &arguments) const {
  // Overridden where necessary.
}

static void addRuntimeLibraryFlags(const OutputInfo &OI,
                                   ArgStringList &Arguments) {
  if (!OI.RuntimeVariant)
    return;

  const OutputInfo::MSVCRuntime RT = OI.RuntimeVariant.value();

  Arguments.push_back("-autolink-library");
  Arguments.push_back("oldnames");

  Arguments.push_back("-autolink-library");
  switch (RT) {
  case OutputInfo::MSVCRuntime::MultiThreaded:
    Arguments.push_back("libcmt");
    break;

  case OutputInfo::MSVCRuntime::MultiThreadedDebug:
    Arguments.push_back("libcmtd");
    break;

  case OutputInfo::MSVCRuntime::MultiThreadedDLL:
    Arguments.push_back("msvcrt");
    break;

  case OutputInfo::MSVCRuntime::MultiThreadedDebugDLL:
    Arguments.push_back("msvcrtd");
    break;
  }

  // NOTE(compnerd) we do not support /ML and /MLd
  Arguments.push_back("-Xcc");
  Arguments.push_back("-D_MT");

  if (RT == OutputInfo::MSVCRuntime::MultiThreadedDLL ||
      RT == OutputInfo::MSVCRuntime::MultiThreadedDebugDLL) {
    Arguments.push_back("-Xcc");
    Arguments.push_back("-D_DLL");
  }
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const CompileJobAction &job,
                               const JobContext &context) const {
  InvocationInfo II{SWIFT_EXECUTABLE_NAME};
  ArgStringList &Arguments = II.Arguments;
  II.allowsResponseFiles = true;

  for (auto &s : getDriver().getSwiftProgramArgs())
    Arguments.push_back(s.c_str());
  Arguments.push_back("-frontend");

  {
    // Determine the frontend mode option.
    const char *FrontendModeOption = context.computeFrontendModeForCompile();
    assert(FrontendModeOption != nullptr &&
           "No frontend mode option specified!");
    Arguments.push_back(FrontendModeOption);
  }

  context.addFrontendInputAndOutputArguments(Arguments, II.FilelistInfos);

  // Forward migrator flags.
  if (auto DataPath =
          context.Args.getLastArg(options::OPT_api_diff_data_file)) {
    Arguments.push_back("-api-diff-data-file");
    Arguments.push_back(DataPath->getValue());
  }
  if (auto DataDir = context.Args.getLastArg(options::OPT_api_diff_data_dir)) {
    Arguments.push_back("-api-diff-data-dir");
    Arguments.push_back(DataDir->getValue());
  }
  if (context.Args.hasArg(options::OPT_dump_usr)) {
    Arguments.push_back("-dump-usr");
  }

  if (context.Args.hasArg(options::OPT_parse_stdlib))
    Arguments.push_back("-disable-objc-attr-requires-foundation-module");

  addCommonFrontendArgs(context.OI, context.Output, context.Args, Arguments);
  addRuntimeLibraryFlags(context.OI, Arguments);

  // Pass along an -(internal-)?import-bridging-header arg, replacing the
  // argument with the name of any input PCH to the current action if one is
  // present.
  if (context.Args.hasArgNoClaim(options::OPT_import_bridging_header,
                                 options::OPT_internal_import_bridging_header)) {
    bool ForwardAsIs = true;
    bool bridgingPCHIsEnabled =
        context.Args.hasFlag(options::OPT_enable_bridging_pch,
                             options::OPT_disable_bridging_pch, true);
    bool usePersistentPCH = bridgingPCHIsEnabled &&
                            context.Args.hasArg(options::OPT_pch_output_dir);
    bool isInternalImport = context.Args.getLastArgNoClaim(
        options::OPT_import_bridging_header,
        options::OPT_internal_import_bridging_header)
      ->getOption().getID() == options::OPT_internal_import_bridging_header;
    if (!usePersistentPCH) {
      for (auto *IJ : context.Inputs) {
        if (!IJ->getOutput().getAnyOutputForType(file_types::TY_PCH).empty()) {
          if (isInternalImport)
            Arguments.push_back("-internal-import-bridging-header");
          else
            Arguments.push_back("-import-bridging-header");
          addInputsOfType(Arguments, context.Inputs, context.Args,
                          file_types::TY_PCH);
          ForwardAsIs = false;
          break;
        }
      }
    }
    if (ForwardAsIs) {
      context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header,
                              options::OPT_internal_import_bridging_header);
    }
    if (usePersistentPCH) {
      context.Args.AddLastArg(Arguments, options::OPT_pch_output_dir);
      switch (context.OI.CompilerMode) {
      case OutputInfo::Mode::StandardCompile:
        // In the 'multiple invocations for each file' mode we don't need to
        // validate the PCH every time, it has been validated with the initial
        // -emit-pch invocation.
        Arguments.push_back("-pch-disable-validation");
        break;

      case OutputInfo::Mode::Immediate:
      case OutputInfo::Mode::REPL:
      case OutputInfo::Mode::SingleCompile:
        break;
      }
    }
  }

  if (context.Args.hasArg(options::OPT_parse_as_library) ||
      context.Args.hasArg(options::OPT_emit_library))
    Arguments.push_back("-parse-as-library");

  context.Args.AddLastArg(Arguments, options::OPT_parse_sil);

  Arguments.push_back("-module-name");
  Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));

  if (context.Args.hasArg(options::OPT_CrossModuleOptimization)) {
    Arguments.push_back("-cross-module-optimization");
  }

  if (context.Args.hasArg(options::OPT_ExperimentalPerformanceAnnotations)) {
    Arguments.push_back("-experimental-performance-annotations");
  }

  file_types::ID remarksFileType = file_types::TY_YAMLOptRecord;
  // If a specific format is specified for the remarks, forward that as is.
  if (auto remarksFormat =
          context.Args.getLastArg(options::OPT_save_optimization_record_EQ)) {
    Arguments.push_back(context.Args.MakeArgString(
        Twine("-save-optimization-record=") + remarksFormat->getValue()));
    // If that's the case, add the proper output file for the type.
    if (llvm::Expected<file_types::ID> fileType =
            remarkFileTypeFromArgs(context.Args))
      remarksFileType = *fileType;
    else
      consumeError(fileType.takeError()); // Don't report errors here. This will
                                          // be reported later anyway.
  }
  addOutputsOfType(Arguments, context.Output, context.Args, remarksFileType,
                   "-save-optimization-record-path");

  if (auto remarksFilter = context.Args.getLastArg(
          options::OPT_save_optimization_record_passes)) {
    Arguments.push_back("-save-optimization-record-passes");
    Arguments.push_back(remarksFilter->getValue());
  }

  if (context.Args.hasArg(options::OPT_migrate_keep_objc_visibility)) {
    Arguments.push_back("-migrate-keep-objc-visibility");
  }

  addOutputsOfType(Arguments, context.Output, context.Args,
                   file_types::TY_Remapping, "-emit-remap-file-path");

  if (context.OI.numThreads > 0) {
    Arguments.push_back("-num-threads");
    Arguments.push_back(
        context.Args.MakeArgString(Twine(context.OI.numThreads)));
  }

  // Add the output file argument if necessary.
  if (context.Output.getPrimaryOutputType() != file_types::TY_Nothing) {
    auto IndexUnitOutputs = context.Output.getIndexUnitOutputFilenames();

    if (context.shouldUseMainOutputFileListInFrontendInvocation()) {
      Arguments.push_back("-output-filelist");
      Arguments.push_back(context.getTemporaryFilePath("outputs", ""));
      II.FilelistInfos.push_back({Arguments.back(),
                                  context.Output.getPrimaryOutputType(),
                                  FilelistInfo::WhichFiles::Output});
      if (!IndexUnitOutputs.empty()) {
        Arguments.push_back("-index-unit-output-path-filelist");
        Arguments.push_back(context.getTemporaryFilePath("index-unit-outputs",
                                                         ""));
        II.FilelistInfos.push_back({
          Arguments.back(), file_types::TY_IndexUnitOutputPath,
          FilelistInfo::WhichFiles::IndexUnitOutputPaths});
      }
    } else {
      for (auto FileName : context.Output.getPrimaryOutputFilenames()) {
        Arguments.push_back("-o");
        Arguments.push_back(context.Args.MakeArgString(FileName));
      }

      for (auto FileName : IndexUnitOutputs) {
        Arguments.push_back("-index-unit-output-path");
        Arguments.push_back(context.Args.MakeArgString(FileName));
      }
    }
  }

  if (context.Args.hasArg(options::OPT_embed_bitcode_marker))
    Arguments.push_back("-embed-bitcode-marker");

  // For `-index-file` mode add `-disable-typo-correction`, since the errors
  // will be ignored and it can be expensive to do typo-correction.
  if (job.getType() == file_types::TY_IndexData) {
    Arguments.push_back("-disable-typo-correction");
  }

  if (context.Args.hasArg(options::OPT_index_store_path)) {
    context.Args.AddLastArg(Arguments, options::OPT_index_store_path);
    if (!context.Args.hasArg(options::OPT_index_ignore_system_modules))
      Arguments.push_back("-index-system-modules");
    context.Args.AddLastArg(Arguments, options::OPT_index_ignore_clang_modules);
    context.Args.AddLastArg(Arguments, options::OPT_index_include_locals);
  }

  if (context.Args.hasArg(options::OPT_debug_info_store_invocation) ||
      shouldStoreInvocationInDebugInfo()) {
    Arguments.push_back("-debug-info-store-invocation");
  }

  if (context.Args.hasArg(
                      options::OPT_disable_autolinking_runtime_compatibility)) {
    Arguments.push_back("-disable-autolinking-runtime-compatibility");
  }

  if (auto arg = context.Args.getLastArg(
                                  options::OPT_runtime_compatibility_version)) {
    Arguments.push_back("-runtime-compatibility-version");
    Arguments.push_back(arg->getValue());
  }
  if (context.Args.hasArg(options::OPT_track_system_dependencies)) {
    Arguments.push_back("-track-system-dependencies");
  }

  if (context.Args.hasFlag(options::OPT_static_executable,
                           options::OPT_no_static_executable, false) ||
      context.Args.hasFlag(options::OPT_static_stdlib,
                           options::OPT_no_static_stdlib, false)) {
    Arguments.push_back("-use-static-resource-dir");
  }

  context.Args.AddLastArg(
      Arguments,
      options::
          OPT_disable_autolinking_runtime_compatibility_dynamic_replacements);
  context.Args.AddLastArg(
      Arguments,
      options::OPT_disable_autolinking_runtime_compatibility_concurrency);

  if (context.OI.CompilerMode == OutputInfo::Mode::SingleCompile) {
    context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph);
    context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph_dir);
  }
  context.Args.AddLastArg(Arguments, options::OPT_include_spi_symbols);
  context.Args.AddLastArg(Arguments, options::OPT_emit_extension_block_symbols,
                          options::OPT_omit_extension_block_symbols);
  context.Args.AddLastArg(Arguments, options::OPT_symbol_graph_minimum_access_level);
  context.Args.AddLastArg(Arguments, options::OPT_symbol_graph_skip_synthesized_members);
  context.Args.AddLastArg(Arguments, options::OPT_symbol_graph_skip_inherited_docs);
  context.Args.AddLastArg(Arguments, options::OPT_symbol_graph_pretty_print);

  return II;
}

const char *ToolChain::JobContext::computeFrontendModeForCompile() const {
  switch (OI.CompilerMode) {
  case OutputInfo::Mode::StandardCompile:
  case OutputInfo::Mode::SingleCompile:
    break;
  case OutputInfo::Mode::Immediate:
  case OutputInfo::Mode::REPL:
    llvm_unreachable("REPL and immediate modes handled elsewhere");
  }
  switch (Output.getPrimaryOutputType()) {
  case file_types::TY_Object:
    return "-c";
  case file_types::TY_PCH:
    return "-emit-pch";
  case file_types::TY_ASTDump:
    return "-dump-ast";
  case file_types::TY_RawSIL:
    return "-emit-silgen";
  case file_types::TY_SIL:
    return "-emit-sil";
  case file_types::TY_LoweredSIL:
    return "-emit-lowered-sil";
  case file_types::TY_RawSIB:
    return "-emit-sibgen";
  case file_types::TY_SIB:
    return "-emit-sib";
  case file_types::TY_RawLLVM_IR:
    return "-emit-irgen";
  case file_types::TY_LLVM_IR:
    return "-emit-ir";
  case file_types::TY_LLVM_BC:
    return "-emit-bc";
  case file_types::TY_ClangModuleFile:
    return "-emit-pcm";
  case file_types::TY_Assembly:
    return "-S";
  case file_types::TY_SwiftModuleFile:
    // Since this is our primary output, we need to specify the option here.
    return "-emit-module";
  case file_types::TY_ImportedModules:
    return "-emit-imported-modules";
  case file_types::TY_JSONDependencies:
    return "-scan-dependencies";
  case file_types::TY_JSONArguments:
    return "-emit-supported-arguments";
  case file_types::TY_IndexData:
    return "-typecheck";
  case file_types::TY_Remapping:
    return "-update-code";
  case file_types::TY_Nothing:
    // We were told to output nothing, so get the last mode option and use that.
    if (const Arg *A = Args.getLastArg(options::OPT_modes_Group))
      return A->getSpelling().data();
    else
      llvm_unreachable("We were told to perform a standard compile, "
                       "but no mode option was passed to the driver.");
  case file_types::TY_Swift:
  case file_types::TY_dSYM:
  case file_types::TY_AutolinkFile:
  case file_types::TY_Dependencies:
  case file_types::TY_SwiftModuleDocFile:
  case file_types::TY_SerializedDiagnostics:
  case file_types::TY_ClangHeader:
  case file_types::TY_Image:
  case file_types::TY_SwiftDeps:
  case file_types::TY_ExternalSwiftDeps:
  case file_types::TY_ModuleTrace:
  case file_types::TY_FineModuleTrace:
  case file_types::TY_TBD:
  case file_types::TY_YAMLOptRecord:
  case file_types::TY_BitstreamOptRecord:
  case file_types::TY_SwiftModuleInterfaceFile:
  case file_types::TY_PrivateSwiftModuleInterfaceFile:
  case file_types::TY_PackageSwiftModuleInterfaceFile:
  case file_types::TY_SwiftModuleSummaryFile:
  case file_types::TY_SwiftSourceInfoFile:
  case file_types::TY_SwiftCrossImportDir:
  case file_types::TY_SwiftOverlayFile:
  case file_types::TY_IndexUnitOutputPath:
  case file_types::TY_SwiftABIDescriptor:
  case file_types::TY_SwiftAPIDescriptor:
  case file_types::TY_ConstValues:
  case file_types::TY_SwiftFixIt:
  case file_types::TY_ModuleSemanticInfo:
  case file_types::TY_CachedDiagnostics:
  case file_types::TY_SymbolGraphFile:
    llvm_unreachable("Output type can never be primary output.");
  case file_types::TY_INVALID:
    llvm_unreachable("Invalid type ID");
  }
  llvm_unreachable("unhandled output type");
}

void ToolChain::JobContext::addFrontendInputAndOutputArguments(
    ArgStringList &Arguments, std::vector<FilelistInfo> &FilelistInfos) const {

  switch (OI.CompilerMode) {
  case OutputInfo::Mode::StandardCompile:
    assert(InputActions.size() == 1 &&
           "Standard-compile mode takes exactly one input (the primary file)");
    break;
  case OutputInfo::Mode::SingleCompile:
    break;
  case OutputInfo::Mode::Immediate:
  case OutputInfo::Mode::REPL:
    llvm_unreachable("REPL and immediate modes handled elsewhere");
  }

  const bool UseFileList = shouldUseInputFileList();
  const bool MayHavePrimaryInputs = OI.mightHaveExplicitPrimaryInputs(Output);
  const bool UsePrimaryFileList =
      MayHavePrimaryInputs &&
      shouldUsePrimaryInputFileListInFrontendInvocation();
  const bool FilterInputsByType = shouldFilterFrontendInputsByType();
  const bool UseSupplementaryOutputFileList =
      shouldUseSupplementaryOutputFileMapInFrontendInvocation();

  assert((C.getFilelistThreshold() != Compilation::NEVER_USE_FILELIST ||
          !UseFileList && !UsePrimaryFileList &&
              !UseSupplementaryOutputFileList) &&
         "No filelists are used if FilelistThreshold=NEVER_USE_FILELIST");

  if (UseFileList) {
    Arguments.push_back("-filelist");
    Arguments.push_back(getAllSourcesPath());
  }
  if (UsePrimaryFileList) {
    Arguments.push_back("-primary-filelist");
    Arguments.push_back(getTemporaryFilePath("primaryInputs", ""));
    FilelistInfos.push_back({Arguments.back(), file_types::TY_Swift,
                             FilelistInfo::WhichFiles::SourceInputActions});
  }
  if (!UseFileList || !UsePrimaryFileList) {
    addFrontendCommandLineInputArguments(MayHavePrimaryInputs, UseFileList,
                                         UsePrimaryFileList, FilterInputsByType,
                                         Arguments);
  }

  if (UseSupplementaryOutputFileList) {
    Arguments.push_back("-supplementary-output-file-map");
    Arguments.push_back(getTemporaryFilePath("supplementaryOutputs", ""));
    FilelistInfos.push_back({Arguments.back(), file_types::TY_INVALID,
                             FilelistInfo::WhichFiles::SupplementaryOutput});
  } else {
    addFrontendSupplementaryOutputArguments(Arguments);
  }
}

void ToolChain::JobContext::addFrontendCommandLineInputArguments(
    const bool mayHavePrimaryInputs, const bool useFileList,
    const bool usePrimaryFileList, const bool filterByType,
    ArgStringList &arguments) const {
  llvm::DenseSet<StringRef> primaries;

  if (mayHavePrimaryInputs) {
    for (const Action *A : InputActions) {
      const auto *IA = cast<InputAction>(A);
      const llvm::opt::Arg &InArg = IA->getInputArg();
      primaries.insert(InArg.getValue());
    }
  }
  // -index-file compilations are weird. They are processed as SingleCompiles
  // (WMO), but must indicate that there is one primary file, designated by
  // -index-file-path.
  if (Arg *A = Args.getLastArg(options::OPT_index_file_path)) {
    assert(primaries.empty() &&
           "index file jobs should be treated as single (WMO) compiles");
    primaries.insert(A->getValue());
  }
  for (auto inputPair : getTopLevelInputFiles()) {
    if (filterByType && !file_types::isPartOfSwiftCompilation(inputPair.first))
      continue;
    const char *inputName = inputPair.second->getValue();
    const bool isPrimary = primaries.count(inputName);
    if (isPrimary && !usePrimaryFileList) {
      arguments.push_back("-primary-file");
      arguments.push_back(inputName);
    }
    if ((!isPrimary || usePrimaryFileList) && !useFileList)
      arguments.push_back(inputName);
  }
}

void ToolChain::JobContext::addFrontendSupplementaryOutputArguments(
    ArgStringList &arguments) const {
  // FIXME: Get these and other argument strings from the same place for both
  // driver and frontend.
  addOutputsOfType(arguments, Output, Args, file_types::ID::TY_SwiftModuleFile,
                   "-emit-module-path");

  addOutputsOfType(arguments, Output, Args, file_types::TY_SwiftModuleDocFile,
                   "-emit-module-doc-path");

  addOutputsOfType(arguments, Output, Args, file_types::TY_SwiftSourceInfoFile,
                   "-emit-module-source-info-path");

  addOutputsOfType(arguments, Output, Args,
                   file_types::ID::TY_SwiftModuleInterfaceFile,
                   "-emit-module-interface-path");

  addOutputsOfType(arguments, Output, Args,
                   file_types::ID::TY_PrivateSwiftModuleInterfaceFile,
                   "-emit-private-module-interface-path");

  addOutputsOfType(arguments, Output, Args,
                   file_types::ID::TY_PackageSwiftModuleInterfaceFile,
                   "-emit-package-module-interface-path");

  addOutputsOfType(arguments, Output, Args,
                   file_types::TY_SerializedDiagnostics,
                   "-serialize-diagnostics-path");

  if (addOutputsOfType(arguments, Output, Args, file_types::ID::TY_ClangHeader,
                       "-emit-objc-header-path")) {
    assert(OI.CompilerMode == OutputInfo::Mode::SingleCompile &&
           "The Swift tool should only emit an Obj-C header in single compile"
           "mode!");
  }

  addOutputsOfType(arguments, Output, Args, file_types::TY_Dependencies,
                   "-emit-dependencies-path");
  addOutputsOfType(arguments, Output, Args, file_types::TY_SwiftDeps,
                   "-emit-reference-dependencies-path");
  addOutputsOfType(arguments, Output, Args, file_types::TY_ModuleTrace,
                   "-emit-loaded-module-trace-path");
  addOutputsOfType(arguments, Output, Args, file_types::TY_TBD,
                   "-emit-tbd-path");
  addOutputsOfType(arguments, Output, Args,
                   file_types::TY_SwiftModuleSummaryFile,
                   "-emit-module-summary-path");
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const InterpretJobAction &job,
                               const JobContext &context) const {
  assert(context.OI.CompilerMode == OutputInfo::Mode::Immediate);

  InvocationInfo II{SWIFT_EXECUTABLE_NAME};
  ArgStringList &Arguments = II.Arguments;
  II.allowsResponseFiles = true;

  for (auto &s : getDriver().getSwiftProgramArgs())
    Arguments.push_back(s.c_str());
  Arguments.push_back("-frontend");
  Arguments.push_back("-interpret");

  assert(context.Inputs.empty() &&
         "The Swift frontend does not expect to be fed any input Jobs!");

  for (const Action *A : context.InputActions) {
    cast<InputAction>(A)->getInputArg().render(context.Args, Arguments);
  }

  if (context.Args.hasArg(options::OPT_parse_stdlib))
    Arguments.push_back("-disable-objc-attr-requires-foundation-module");

  addCommonFrontendArgs(context.OI, context.Output, context.Args, Arguments);
  addRuntimeLibraryFlags(context.OI, Arguments);

  context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header,
                          options::OPT_internal_import_bridging_header);

  context.Args.AddLastArg(Arguments, options::OPT_parse_sil);

  Arguments.push_back("-module-name");
  Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));

  context.Args.AddAllArgs(Arguments, options::OPT_framework);
  ToolChain::addLinkedLibArgs(context.Args, Arguments);

  // The immediate arguments must be last.
  context.Args.AddLastArg(Arguments, options::OPT__DASH_DASH);

  return II;
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const BackendJobAction &job,
                               const JobContext &context) const {
  assert(context.Args.hasArg(options::OPT_embed_bitcode));
  ArgStringList Arguments;

  for (auto &s : getDriver().getSwiftProgramArgs())
    Arguments.push_back(s.c_str());
  Arguments.push_back("-frontend");

  // Determine the frontend mode option.
  const char *FrontendModeOption = nullptr;
  switch (context.OI.CompilerMode) {
  case OutputInfo::Mode::StandardCompile:
  case OutputInfo::Mode::SingleCompile: {
    switch (context.Output.getPrimaryOutputType()) {
    case file_types::TY_Object:
      FrontendModeOption = "-c";
      break;
    case file_types::TY_RawLLVM_IR:
      FrontendModeOption = "-emit-irgen";
      break;
    case file_types::TY_LLVM_IR:
      FrontendModeOption = "-emit-ir";
      break;
    case file_types::TY_LLVM_BC:
      FrontendModeOption = "-emit-bc";
      break;
    case file_types::TY_Assembly:
      FrontendModeOption = "-S";
      break;
    case file_types::TY_Nothing:
      // We were told to output nothing, so get the last mode option and use
      // that.
      if (const Arg *A = context.Args.getLastArg(options::OPT_modes_Group))
        FrontendModeOption = A->getSpelling().data();
      else
        llvm_unreachable("We were told to perform a standard compile, "
                         "but no mode option was passed to the driver.");
      break;

    case file_types::TY_ImportedModules:
    case file_types::TY_TBD:
    case file_types::TY_SwiftModuleFile:
    case file_types::TY_ASTDump:
    case file_types::TY_RawSIL:
    case file_types::TY_RawSIB:
    case file_types::TY_SIL:
    case file_types::TY_LoweredSIL:
    case file_types::TY_SIB:
    case file_types::TY_PCH:
    case file_types::TY_ClangModuleFile:
    case file_types::TY_IndexData:
    case file_types::TY_JSONDependencies:
    case file_types::TY_JSONArguments:
      llvm_unreachable("Cannot be output from backend job");
    case file_types::TY_Swift:
    case file_types::TY_dSYM:
    case file_types::TY_AutolinkFile:
    case file_types::TY_Dependencies:
    case file_types::TY_SwiftModuleDocFile:
    case file_types::TY_SerializedDiagnostics:
    case file_types::TY_ClangHeader:
    case file_types::TY_Image:
    case file_types::TY_SwiftDeps:
    case file_types::TY_ExternalSwiftDeps:
    case file_types::TY_Remapping:
    case file_types::TY_ModuleTrace:
    case file_types::TY_FineModuleTrace:
    case file_types::TY_YAMLOptRecord:
    case file_types::TY_BitstreamOptRecord:
    case file_types::TY_SwiftModuleInterfaceFile:
    case file_types::TY_PrivateSwiftModuleInterfaceFile:
    case file_types::TY_PackageSwiftModuleInterfaceFile:
    case file_types::TY_SwiftModuleSummaryFile:
    case file_types::TY_SwiftSourceInfoFile:
    case file_types::TY_SwiftCrossImportDir:
    case file_types::TY_SwiftOverlayFile:
    case file_types::TY_IndexUnitOutputPath:
    case file_types::TY_SwiftABIDescriptor:
    case file_types::TY_SwiftAPIDescriptor:
    case file_types::TY_ConstValues:
    case file_types::TY_SwiftFixIt:
    case file_types::TY_ModuleSemanticInfo:
    case file_types::TY_CachedDiagnostics:
    case file_types::TY_SymbolGraphFile:
      llvm_unreachable("Output type can never be primary output.");
    case file_types::TY_INVALID:
      llvm_unreachable("Invalid type ID");
    }
    break;
  }
  case OutputInfo::Mode::Immediate:
  case OutputInfo::Mode::REPL:
    llvm_unreachable("invalid mode for backend job");
  }

  assert(FrontendModeOption != nullptr && "No frontend mode option specified!");

  Arguments.push_back(FrontendModeOption);

  // Add input arguments.
  switch (context.OI.CompilerMode) {
  case OutputInfo::Mode::StandardCompile: {
    assert(context.Inputs.size() == 1 && "The backend expects one input!");
    Arguments.push_back("-primary-file");
    const Job *Cmd = context.Inputs.front();
    Arguments.push_back(context.Args.MakeArgString(
        Cmd->getOutput().getPrimaryOutputFilename()));
    break;
  }
  case OutputInfo::Mode::SingleCompile: {
    assert(context.Inputs.size() == 1 && "The backend expects one input!");
    Arguments.push_back("-primary-file");
    const Job *Cmd = context.Inputs.front();

    // In multi-threaded compilation, the backend job must select the correct
    // output file of the compilation job.
    auto OutNames = Cmd->getOutput().getPrimaryOutputFilenames();
    Arguments.push_back(
        context.Args.MakeArgString(OutNames[job.getInputIndex()]));
    break;
  }
  case OutputInfo::Mode::Immediate:
  case OutputInfo::Mode::REPL:
    llvm_unreachable("invalid mode for backend job");
  }

  // Add flags implied by -embed-bitcode.
  Arguments.push_back("-embed-bitcode");

  // -embed-bitcode only supports a restricted set of flags.
  Arguments.push_back("-target");
  Arguments.push_back(context.Args.MakeArgString(getTriple().str()));

  // Enable address top-byte ignored in the ARM64 backend.
  if (getTriple().getArch() == llvm::Triple::aarch64 ||
      getTriple().getArch() == llvm::Triple::aarch64_32) {
    Arguments.push_back("-Xllvm");
    Arguments.push_back("-aarch64-use-tbi");
  }

  // Handle the CPU and its preferences.
  context.Args.AddLastArg(Arguments, options::OPT_target_cpu);

  // Enable optimizations, but disable all LLVM-IR-level transformations.
  context.Args.AddLastArg(Arguments, options::OPT_O_Group);
  Arguments.push_back("-disable-llvm-optzns");

  context.Args.AddLastArg(Arguments, options::OPT_parse_stdlib);

  Arguments.push_back("-module-name");
  Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));

  // Add the output file argument if necessary.
  if (context.Output.getPrimaryOutputType() != file_types::TY_Nothing) {
    for (auto FileName : context.Output.getPrimaryOutputFilenames()) {
      Arguments.push_back("-o");
      Arguments.push_back(context.Args.MakeArgString(FileName));
    }
  }

  return {SWIFT_EXECUTABLE_NAME, Arguments};
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const MergeModuleJobAction &job,
                               const JobContext &context) const {
  InvocationInfo II{SWIFT_EXECUTABLE_NAME};
  ArgStringList &Arguments = II.Arguments;
  II.allowsResponseFiles = true;

  for (auto &s : getDriver().getSwiftProgramArgs())
    Arguments.push_back(s.c_str());
  Arguments.push_back("-frontend");

  Arguments.push_back("-merge-modules");
  Arguments.push_back("-emit-module");

  if (context.shouldUseInputFileList()) {
    Arguments.push_back("-filelist");
    Arguments.push_back(context.getTemporaryFilePath("inputs", ""));
    II.FilelistInfos.push_back({Arguments.back(),
                                file_types::TY_SwiftModuleFile,
                                FilelistInfo::WhichFiles::InputJobs});

    addInputsOfType(Arguments, context.InputActions,
                    file_types::TY_SwiftModuleFile);
  } else {
    size_t origLen = Arguments.size();
    (void)origLen;
    addInputsOfType(Arguments, context.Inputs, context.Args,
                    file_types::TY_SwiftModuleFile);
    addInputsOfType(Arguments, context.InputActions,
                    file_types::TY_SwiftModuleFile);
    assert(Arguments.size() - origLen >=
               context.Inputs.size() + context.InputActions.size() ||
           context.OI.CompilerOutputType == file_types::TY_Nothing);
    assert((Arguments.size() - origLen == context.Inputs.size() ||
            context.OI.CompilerOutputType == file_types::TY_Nothing ||
            !context.InputActions.empty()) &&
           "every input to MergeModule must generate a swiftmodule");
  }

  // Tell all files to parse as library, which is necessary to load them as
  // serialized ASTs.
  Arguments.push_back("-parse-as-library");

  // Disable SIL optimization passes; we've already optimized the code in each
  // partial mode.
  Arguments.push_back("-disable-diagnostic-passes");
  Arguments.push_back("-disable-sil-perf-optzns");

  addCommonFrontendArgs(context.OI, context.Output, context.Args, Arguments);
  addRuntimeLibraryFlags(context.OI, Arguments);

  addOutputsOfType(Arguments, context.Output, context.Args,
                   file_types::TY_SwiftModuleDocFile, "-emit-module-doc-path");
  addOutputsOfType(Arguments, context.Output, context.Args,
                   file_types::TY_SwiftSourceInfoFile,
                   "-emit-module-source-info-path");
  addOutputsOfType(Arguments, context.Output, context.Args,
                   file_types::ID::TY_SwiftModuleInterfaceFile,
                   "-emit-module-interface-path");
  addOutputsOfType(Arguments, context.Output, context.Args,
                   file_types::ID::TY_PrivateSwiftModuleInterfaceFile,
                   "-emit-private-module-interface-path");
  addOutputsOfType(Arguments, context.Output, context.Args,
                   file_types::ID::TY_PackageSwiftModuleInterfaceFile,
                   "-emit-package-module-interface-path");
  addOutputsOfType(Arguments, context.Output, context.Args,
                   file_types::TY_SerializedDiagnostics,
                   "-serialize-diagnostics-path");
  addOutputsOfType(Arguments, context.Output, context.Args,
                   file_types::TY_ClangHeader, "-emit-objc-header-path");
  addOutputsOfType(Arguments, context.Output, context.Args, file_types::TY_TBD,
                   "-emit-tbd-path");

  context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph);
  context.Args.AddLastArg(Arguments, options::OPT_emit_symbol_graph_dir);
  context.Args.AddLastArg(Arguments, options::OPT_include_spi_symbols);
  context.Args.AddLastArg(Arguments, options::OPT_emit_extension_block_symbols,
                          options::OPT_omit_extension_block_symbols);
  context.Args.AddLastArg(Arguments, options::OPT_symbol_graph_minimum_access_level);

  context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header,
                          options::OPT_internal_import_bridging_header);

  Arguments.push_back("-module-name");
  Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));

  assert(context.Output.getPrimaryOutputType() ==
             file_types::TY_SwiftModuleFile &&
         "The MergeModule tool only produces swiftmodule files!");

  Arguments.push_back("-o");
  Arguments.push_back(
      context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));

  return II;
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const VerifyModuleInterfaceJobAction &job,
                               const JobContext &context) const {
  InvocationInfo II{SWIFT_EXECUTABLE_NAME};
  ArgStringList &Arguments = II.Arguments;
  II.allowsResponseFiles = true;

  for (auto &s : getDriver().getSwiftProgramArgs())
    Arguments.push_back(s.c_str());
  Arguments.push_back("-frontend");

  Arguments.push_back("-typecheck-module-from-interface");

  size_t sizeBefore = Arguments.size();
  addInputsOfType(Arguments, context.Inputs, context.Args, job.getInputType());

  (void)sizeBefore;
  assert(Arguments.size() - sizeBefore == 1 &&
         "should verify exactly one module interface per job");

  addCommonFrontendArgs(context.OI, context.Output, context.Args, Arguments);
  addRuntimeLibraryFlags(context.OI, Arguments);

  addOutputsOfType(Arguments, context.Output, context.Args,
                   file_types::TY_SerializedDiagnostics,
                   "-serialize-diagnostics-path");

  context.Args.AddLastArg(Arguments, options::OPT_import_bridging_header,
                          options::OPT_internal_import_bridging_header);

  Arguments.push_back("-module-name");
  Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName));

  return II;
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const ModuleWrapJobAction &job,
                               const JobContext &context) const {
  InvocationInfo II{SWIFT_EXECUTABLE_NAME};
  ArgStringList &Arguments = II.Arguments;
  II.allowsResponseFiles = true;

  for (auto &s : getDriver().getSwiftProgramArgs())
    Arguments.push_back(s.c_str());
  Arguments.push_back("-modulewrap");

  addInputsOfType(Arguments, context.Inputs, context.Args,
                  file_types::TY_SwiftModuleFile);
  addInputsOfType(Arguments, context.InputActions,
                  file_types::TY_SwiftModuleFile);
  assert(Arguments.size() == 2 &&
         "ModuleWrap expects exactly one merged swiftmodule as input");

  assert(context.Output.getPrimaryOutputType() == file_types::TY_Object &&
         "The -modulewrap mode only produces object files");

  Arguments.push_back("-target");
  Arguments.push_back(context.Args.MakeArgString(getTriple().str()));

  Arguments.push_back("-o");
  Arguments.push_back(
      context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));

  return II;
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const REPLJobAction &job,
                               const JobContext &context) const {
  assert(context.Inputs.empty());
  assert(context.InputActions.empty());

  bool useLLDB;

  switch (job.getRequestedMode()) {
  case REPLJobAction::Mode::Integrated:
    useLLDB = false;
    break;
  case REPLJobAction::Mode::RequireLLDB:
    useLLDB = true;
    break;
  case REPLJobAction::Mode::PreferLLDB:
    useLLDB = !findProgramRelativeToSwift("lldb").empty();
    break;
  }

  ArgStringList FrontendArgs;
  for (auto &s : getDriver().getSwiftProgramArgs())
    FrontendArgs.push_back(s.c_str());

  addCommonFrontendArgs(context.OI, context.Output, context.Args, FrontendArgs);
  addRuntimeLibraryFlags(context.OI, FrontendArgs);

  context.Args.AddLastArg(FrontendArgs, options::OPT_import_bridging_header,
                          options::OPT_internal_import_bridging_header);
  context.Args.addAllArgs(FrontendArgs,
                          {options::OPT_framework, options::OPT_L});
  ToolChain::addLinkedLibArgs(context.Args, FrontendArgs);

  if (!useLLDB) {
    FrontendArgs.insert(FrontendArgs.begin(), {"-frontend", "-repl"});
    FrontendArgs.push_back("-module-name");
    FrontendArgs.push_back(context.Args.MakeArgString(context.OI.ModuleName));
    return {SWIFT_EXECUTABLE_NAME, FrontendArgs};
  }

  // Squash important frontend options into a single argument for LLDB.
  std::string SingleArg = "--repl=";
  {
    llvm::raw_string_ostream os(SingleArg);
    Job::printArguments(os, FrontendArgs);
  }

  ArgStringList Arguments;
  Arguments.push_back(context.Args.MakeArgString(std::move(SingleArg)));

  return {"lldb", Arguments};
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const GenerateDSYMJobAction &job,
                               const JobContext &context) const {
  assert(context.Inputs.size() == 1);
  assert(context.InputActions.empty());
  assert(context.Output.getPrimaryOutputType() == file_types::TY_dSYM);

  ArgStringList Arguments;

  auto inputPath =
      context.Inputs.front()->getOutput().getPrimaryOutputFilename();
  Arguments.push_back(context.Args.MakeArgString(inputPath));

  Arguments.push_back("-o");
  Arguments.push_back(
      context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));

  return {"dsymutil", Arguments};
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const VerifyDebugInfoJobAction &job,
                               const JobContext &context) const {
  assert(context.Inputs.size() == 1);
  assert(context.InputActions.empty());

  // This mirrors the clang driver's --verify-debug-info option.
  ArgStringList Arguments;
  Arguments.push_back("--verify");
  Arguments.push_back("--debug-info");
  Arguments.push_back("--eh-frame");
  Arguments.push_back("--quiet");

  auto inputPath =
      context.Inputs.front()->getOutput().getPrimaryOutputFilename();
  Arguments.push_back(context.Args.MakeArgString(inputPath));

  return {"dwarfdump", Arguments};
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const GeneratePCHJobAction &job,
                               const JobContext &context) const {
  assert(context.Inputs.empty());
  assert(context.InputActions.size() == 1);
  assert((!job.isPersistentPCH() &&
          context.Output.getPrimaryOutputType() == file_types::TY_PCH) ||
         (job.isPersistentPCH() &&
          context.Output.getPrimaryOutputType() == file_types::TY_Nothing));

  InvocationInfo II{SWIFT_EXECUTABLE_NAME};
  ArgStringList &Arguments = II.Arguments;
  II.allowsResponseFiles = true;

  for (auto &s : getDriver().getSwiftProgramArgs())
    Arguments.push_back(s.c_str());
  Arguments.push_back("-frontend");

  addCommonFrontendArgs(context.OI, context.Output, context.Args, Arguments);
  addRuntimeLibraryFlags(context.OI, Arguments);

  addOutputsOfType(Arguments, context.Output, context.Args,
                   file_types::TY_SerializedDiagnostics,
                   "-serialize-diagnostics-path");

  addInputsOfType(Arguments, context.InputActions, file_types::TY_ClangHeader);
  context.Args.AddLastArg(Arguments, options::OPT_index_store_path);

  if (job.isPersistentPCH()) {
    Arguments.push_back("-emit-pch");
    Arguments.push_back("-pch-output-dir");
    Arguments.push_back(context.Args.MakeArgString(job.getPersistentPCHDir()));
  } else {
    Arguments.push_back("-emit-pch");
    Arguments.push_back("-o");
    Arguments.push_back(
        context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
  }

  return II;
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const AutolinkExtractJobAction &job,
                               const JobContext &context) const {
  llvm_unreachable("autolink extraction not implemented for this toolchain");
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const DynamicLinkJobAction &job,
                               const JobContext &context) const {
  llvm_unreachable("linking not implemented for this toolchain");
}

ToolChain::InvocationInfo
ToolChain::constructInvocation(const StaticLinkJobAction &job,
                               const JobContext &context) const {
   llvm_unreachable("archiving not implemented for this toolchain");
}

void ToolChain::addPathEnvironmentVariableIfNeeded(
    Job::EnvironmentVector &env, const char *name, const char *separator,
    options::ID optionID, const ArgList &args,
    ArrayRef<std::string> extraEntries) const {
  auto linkPathOptions = args.filtered(optionID);
  if (linkPathOptions.begin() == linkPathOptions.end() && extraEntries.empty())
    return;

  std::string newPaths;
  interleave(linkPathOptions,
             [&](const Arg *arg) { newPaths.append(arg->getValue()); },
             [&] { newPaths.append(separator); });
  for (auto extraEntry : extraEntries) {
    if (!newPaths.empty())
      newPaths.append(separator);
    newPaths.append(extraEntry.data(), extraEntry.size());
  }
  if (auto currentPaths = llvm::sys::Process::GetEnv(name)) {
    newPaths.append(separator);
    newPaths.append(currentPaths.value());
  }
  env.emplace_back(name, args.MakeArgString(newPaths));
}

void ToolChain::addLinkRuntimeLib(const ArgList &Args, ArgStringList &Arguments,
                                  StringRef LibName) const {
  SmallString<128> P;
  getClangLibraryPath(Args, P);
  llvm::sys::path::append(P, LibName);
  Arguments.push_back(Args.MakeArgString(P));
}

static void appendInProcPluginServerPath(StringRef PluginPathRoot,
                                         llvm::SmallVectorImpl<char> &InProcPluginServerPath) {
  InProcPluginServerPath.append(PluginPathRoot.begin(), PluginPathRoot.end());
#if defined(_WIN32)
  llvm::sys::path::append(InProcPluginServerPath, "bin", "SwiftInProcPluginServer.dll");
#elif defined(__APPLE__)
  llvm::sys::path::append(InProcPluginServerPath, "lib", "swift", "host", "libSwiftInProcPluginServer.dylib");
#elif defined(__unix__)
  llvm::sys::path::append(InProcPluginServerPath, "lib", "swift", "host", "libSwiftInProcPluginServer.so");
#else
#error Unknown compiler host
#endif
}

static void appendPluginsPath(StringRef PluginPathRoot,
                              llvm::SmallVectorImpl<char> &PluginsPath) {
  PluginsPath.append(PluginPathRoot.begin(), PluginPathRoot.end());
#if defined(_WIN32)
  llvm::sys::path::append(PluginsPath, "bin");
#elif defined(__APPLE__) || defined(__unix__)
  llvm::sys::path::append(PluginsPath, "lib", "swift", "host", "plugins");
#else
#error Unknown compiler host
#endif
}

#if defined(__APPLE__) || defined(__unix__)
static void appendLocalPluginsPath(StringRef PluginPathRoot,
                                   llvm::SmallVectorImpl<char> &LocalPluginsPath) {
  SmallString<261> localPluginPathRoot = PluginPathRoot;
  llvm::sys::path::append(localPluginPathRoot, "local");
  appendPluginsPath(localPluginPathRoot, LocalPluginsPath);
}
#endif

void ToolChain::addPluginArguments(const ArgList &Args,
                                   ArgStringList &Arguments) const {
  SmallString<261> pluginPathRoot = StringRef(getDriver().getSwiftProgramPath());
  llvm::sys::path::remove_filename(pluginPathRoot); // Remove `swift`
  llvm::sys::path::remove_filename(pluginPathRoot); // Remove `bin`

  // In-process plugin server path.
  SmallString<261> inProcPluginServerPath;
  appendInProcPluginServerPath(pluginPathRoot, inProcPluginServerPath);
  Arguments.push_back("-in-process-plugin-server-path");
  Arguments.push_back(Args.MakeArgString(inProcPluginServerPath));

  // Default plugin path.
  SmallString<261> defaultPluginPath;
  appendPluginsPath(pluginPathRoot, defaultPluginPath);
  Arguments.push_back("-plugin-path");
  Arguments.push_back(Args.MakeArgString(defaultPluginPath));

  // Local plugin path.
#if defined(__APPLE__) || defined(__unix__)
  SmallString<261> localPluginPath;
  appendLocalPluginsPath(pluginPathRoot, localPluginPath);
  Arguments.push_back("-plugin-path");
  Arguments.push_back(Args.MakeArgString(localPluginPath));
#endif
}

void ToolChain::getClangLibraryPath(const ArgList &Args,
                                    SmallString<128> &LibPath) const {
  const llvm::Triple &T = getTriple();

  getResourceDirPath(LibPath, Args, /*Shared=*/true);
  // Remove platform name.
  llvm::sys::path::remove_filename(LibPath);
  StringRef platformName = "darwin";
  if (!T.isOSDarwin())
    platformName = T.isAndroid() ? "linux" : getPlatformNameForTriple(T);
  llvm::sys::path::append(LibPath, "clang", "lib", platformName);
}

/// Get the runtime library link path, which is platform-specific and found
/// relative to the compiler.
void ToolChain::getResourceDirPath(SmallVectorImpl<char> &resourceDirPath,
                                   const llvm::opt::ArgList &args,
                                   bool shared) const {
  if (const Arg *A = args.getLastArg(options::OPT_resource_dir)) {
    StringRef value = A->getValue();
    resourceDirPath.append(value.begin(), value.end());
  } else if (!getTriple().isOSDarwin() && args.hasArg(options::OPT_sdk)) {
    StringRef value = args.getLastArg(options::OPT_sdk)->getValue();
    resourceDirPath.append(value.begin(), value.end());
    llvm::sys::path::append(resourceDirPath, "usr");
    CompilerInvocation::appendSwiftLibDir(resourceDirPath, shared);
  } else {
    auto programPath = getDriver().getSwiftProgramPath();
    CompilerInvocation::computeRuntimeResourcePathFromExecutablePath(
        programPath, shared, resourceDirPath);
  }

  StringRef libSubDir = getPlatformNameForTriple(getTriple());
  if (tripleIsMacCatalystEnvironment(getTriple()))
    libSubDir = "maccatalyst";
  llvm::sys::path::append(resourceDirPath, libSubDir);
}

// Get the secondary runtime library link path given the primary path.
// The compiler will look for runtime libraries in the secondary path if they
// can't be found in the primary path.
void ToolChain::getSecondaryResourceDirPath(
    SmallVectorImpl<char> &secondaryResourceDirPath,
    StringRef primaryPath) const {
  if (!tripleIsMacCatalystEnvironment(getTriple()))
    return;

  // For macCatalyst, the secondary runtime library path is the macOS library
  // path. The compiler will find zippered libraries here.
  secondaryResourceDirPath.append(primaryPath.begin(), primaryPath.end());
  // Remove '/maccatalyst' and replace with 'macosx'.
  llvm::sys::path::remove_filename(secondaryResourceDirPath);
  llvm::sys::path::append(secondaryResourceDirPath, "macosx");
}

void ToolChain::getRuntimeLibraryPaths(SmallVectorImpl<std::string> &runtimeLibPaths,
                                       const llvm::opt::ArgList &args,
                                       StringRef SDKPath, bool shared) const {
  SmallString<128> scratchPath;
  getResourceDirPath(scratchPath, args, shared);
  runtimeLibPaths.push_back(std::string(scratchPath.str()));

  // If there's a secondary resource dir, add it too.
  scratchPath.clear();
  getSecondaryResourceDirPath(scratchPath, runtimeLibPaths[0]);
  if (!scratchPath.empty())
    runtimeLibPaths.push_back(std::string(scratchPath.str()));

  // Only Darwin places libraries directly in /sdk/usr/lib/swift/.
  if (Triple.isOSDarwin() && !SDKPath.empty()) {
    if (!scratchPath.empty()) {
      // If we added the secondary resource dir, we also need the iOSSupport
      // directory.
      scratchPath = SDKPath;
      llvm::sys::path::append(scratchPath, "System", "iOSSupport");
      llvm::sys::path::append(scratchPath, "usr", "lib", "swift");
      runtimeLibPaths.push_back(std::string(scratchPath.str()));
    }

    scratchPath = SDKPath;
    llvm::sys::path::append(scratchPath, "usr", "lib", "swift");
    runtimeLibPaths.push_back(std::string(scratchPath.str()));
  }
}

const char *ToolChain::getClangLinkerDriver(
    const llvm::opt::ArgList &Args) const {
  // We don't use `clang++` unconditionally because we want to avoid pulling in
  // a C++ standard library if it's not needed, in particular because the
  // standard library that `clang++` selects by default may not be the one that
  // is desired.
  bool useCxxLinker = Args.hasArg(options::OPT_enable_experimental_cxx_interop);
  if (Arg *arg = Args.getLastArg(options::OPT_cxx_interoperability_mode))
      useCxxLinker |= StringRef(arg->getValue()) != "off";
  const char *LinkerDriver = useCxxLinker ? "clang++" : "clang";
  if (const Arg *A = Args.getLastArg(options::OPT_tools_directory)) {
    StringRef toolchainPath(A->getValue());

    // If there is a linker driver in the toolchain folder, use that instead.
    if (auto tool = llvm::sys::findProgramByName(LinkerDriver, {toolchainPath}))
      LinkerDriver = Args.MakeArgString(tool.get());
  }

  return LinkerDriver;
}

bool ToolChain::sanitizerRuntimeLibExists(const ArgList &args,
                                          StringRef sanitizerName,
                                          bool shared) const {
  SmallString<128> sanitizerLibPath;
  getClangLibraryPath(args, sanitizerLibPath);
  llvm::sys::path::append(sanitizerLibPath,
                          sanitizerRuntimeLibName(sanitizerName, shared));
  return llvm::sys::fs::exists(sanitizerLibPath.str());
}
