mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
which provides the Neon feature. Do all the necessary plumbing to get this from the driver to the backend. Also, support -arch arm64, and diagnose bad -arch values instead of silently ignoring them. It's not clear to me that we really want to support -arch as an alternative to -target, but unless we rip it out or establish some sort of real policy about it, it really ought to do something approximating the right thing. It would be nice if we could abstract enough of clang's driver that we could re-use some of its basic logic about tool chains and targets instaed of iteratively rediscovering everything it does that's actually critically important. Swift SVN r16447
527 lines
18 KiB
C++
527 lines
18 KiB
C++
//===--- Tools.cpp - Tools Implementations --------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Tools.h"
|
|
#include "ToolChains.h"
|
|
|
|
#include "swift/Basic/Dwarf.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/Basic/Range.h"
|
|
#include "swift/Driver/Driver.h"
|
|
#include "swift/Driver/Job.h"
|
|
#include "swift/Driver/Options.h"
|
|
#include "swift/Frontend/Frontend.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/Option/Arg.h"
|
|
#include "llvm/Option/ArgList.h"
|
|
#include "llvm/Support/Path.h"
|
|
|
|
using namespace swift;
|
|
using namespace swift::driver;
|
|
using namespace swift::driver::tools;
|
|
using namespace llvm::opt;
|
|
|
|
static bool tripleIsiOSSimulator(const llvm::Triple &triple) {
|
|
llvm::Triple::ArchType arch = triple.getArch();
|
|
return (triple.isiOS() &&
|
|
(arch == llvm::Triple::ArchType::x86 ||
|
|
arch == llvm::Triple::ArchType::x86_64));
|
|
}
|
|
|
|
StringRef swift::getPlatformNameForTriple(const llvm::Triple &triple) {
|
|
if (triple.isiOS()) {
|
|
if (tripleIsiOSSimulator(triple))
|
|
return "iphonesimulator";
|
|
return "iphoneos";
|
|
}
|
|
|
|
if (triple.isMacOSX())
|
|
return "macosx";
|
|
|
|
return "";
|
|
}
|
|
|
|
/// Swift Tool
|
|
|
|
static void addInputsOfType(ArgStringList &Arguments, const ActionList &Inputs,
|
|
types::ID InputType) {
|
|
for (auto &Input : Inputs) {
|
|
if (Input->getType() != InputType)
|
|
continue;
|
|
Arguments.push_back(cast<InputAction>(Input)->getInputArg().getValue());
|
|
}
|
|
}
|
|
|
|
static void addInputsOfType(ArgStringList &Arguments, const Job *J,
|
|
types::ID InputType) {
|
|
if (const Command *Cmd = dyn_cast<Command>(J)) {
|
|
auto &output = Cmd->getOutput().getAnyOutputForType(InputType);
|
|
if (!output.empty())
|
|
Arguments.push_back(output.c_str());
|
|
} else if (const JobList *JL = dyn_cast<JobList>(J)) {
|
|
for (const Job *J : *JL)
|
|
addInputsOfType(Arguments, J, InputType);
|
|
} else {
|
|
llvm_unreachable("Unable to add input arguments for unknown Job class");
|
|
}
|
|
}
|
|
|
|
static void addPrimaryInputsOfType(ArgStringList &Arguments, const Job *J,
|
|
types::ID InputType) {
|
|
if (const Command *Cmd = dyn_cast<Command>(J)) {
|
|
auto &outputInfo = Cmd->getOutput();
|
|
if (outputInfo.getPrimaryOutputType() == InputType)
|
|
Arguments.push_back(outputInfo.getPrimaryOutputFilename().c_str());
|
|
} else if (const JobList *JL = dyn_cast<JobList>(J)) {
|
|
for (const Job *J : *JL)
|
|
addPrimaryInputsOfType(Arguments, J, InputType);
|
|
} else {
|
|
llvm_unreachable("Unable to add input arguments for unknown Job class");
|
|
}
|
|
}
|
|
|
|
/// Handle arguments common to all invocations of the frontend (compilation,
|
|
/// module-merging, etc).
|
|
static void addCommonFrontendArgs(const ToolChain &TC,
|
|
const OutputInfo &OI,
|
|
CommandOutput *output,
|
|
const ArgList &inputArgs,
|
|
ArgStringList &arguments) {
|
|
arguments.push_back("-target");
|
|
std::string TripleStr = TC.getTripleString();
|
|
arguments.push_back(inputArgs.MakeArgString(TripleStr));
|
|
|
|
arguments.push_back("-module-name");
|
|
arguments.push_back(inputArgs.MakeArgString(OI.ModuleName));
|
|
|
|
if (!OI.SDKPath.empty()) {
|
|
arguments.push_back("-sdk");
|
|
arguments.push_back(inputArgs.MakeArgString(OI.SDKPath));
|
|
}
|
|
|
|
inputArgs.AddAllArgs(arguments, options::OPT_I);
|
|
inputArgs.AddAllArgs(arguments, options::OPT_F);
|
|
|
|
inputArgs.AddLastArg(arguments, options::OPT_g);
|
|
inputArgs.AddLastArg(arguments, options::OPT_resource_dir);
|
|
inputArgs.AddLastArg(arguments, options::OPT_module_cache_path);
|
|
inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension);
|
|
|
|
// Pass through the values passed to -Xfrontend.
|
|
inputArgs.AddAllArgValues(arguments, options::OPT_Xfrontend);
|
|
|
|
// Pass through any subsystem flags.
|
|
inputArgs.AddAllArgs(arguments, options::OPT_Xllvm);
|
|
inputArgs.AddAllArgs(arguments, options::OPT_Xcc);
|
|
|
|
const std::string &moduleDocOutputPath =
|
|
output->getAdditionalOutputForType(types::TY_SwiftModuleDocFile);
|
|
if (!moduleDocOutputPath.empty()) {
|
|
arguments.push_back("-emit-module-doc");
|
|
arguments.push_back("-emit-module-doc-path");
|
|
arguments.push_back(moduleDocOutputPath.c_str());
|
|
}
|
|
}
|
|
|
|
// Should this be done by the tool chain?
|
|
static void configureDefaultCPU(const llvm::Triple &triple,
|
|
ArgStringList &args) {
|
|
if (triple.isOSDarwin() && triple.getArch() == llvm::Triple::arm64) {
|
|
args.push_back("-target-cpu");
|
|
args.push_back("cyclone");
|
|
args.push_back("-target-feature");
|
|
args.push_back("+neon");
|
|
}
|
|
}
|
|
|
|
Job *Swift::constructJob(const JobAction &JA, std::unique_ptr<JobList> Inputs,
|
|
std::unique_ptr<CommandOutput> Output,
|
|
const ActionList &InputActions, const ArgList &Args,
|
|
const OutputInfo &OI) const {
|
|
ArgStringList Arguments;
|
|
|
|
const char *Exec = getToolChain().getDriver().getSwiftProgramPath().c_str();
|
|
|
|
// Invoke ourselves in -frontend mode.
|
|
Arguments.push_back("-frontend");
|
|
|
|
// Determine the frontend mode option.
|
|
const char *FrontendModeOption = nullptr;
|
|
switch (OI.CompilerMode) {
|
|
case OutputInfo::Mode::StandardCompile:
|
|
case OutputInfo::Mode::SingleCompile: {
|
|
switch (Output->getPrimaryOutputType()) {
|
|
case types::TY_Object:
|
|
FrontendModeOption = "-c";
|
|
break;
|
|
case types::TY_RawSIL:
|
|
FrontendModeOption = "-emit-silgen";
|
|
break;
|
|
case types::TY_SIL:
|
|
FrontendModeOption = "-emit-sil";
|
|
break;
|
|
case types::TY_LLVM_IR:
|
|
FrontendModeOption = "-emit-ir";
|
|
break;
|
|
case types::TY_LLVM_BC:
|
|
FrontendModeOption = "-emit-bc";
|
|
break;
|
|
case types::TY_Assembly:
|
|
FrontendModeOption = "-S";
|
|
break;
|
|
case types::TY_SwiftModuleFile:
|
|
// Since this is our primary output, we need to specify the option here.
|
|
FrontendModeOption = "-emit-module";
|
|
break;
|
|
case 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))
|
|
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 types::TY_Swift:
|
|
case types::TY_dSYM:
|
|
case types::TY_Dependencies:
|
|
case types::TY_SwiftModuleDocFile:
|
|
case types::TY_ClangModuleFile:
|
|
case types::TY_SerializedDiagnostics:
|
|
case types::TY_ObjCHeader:
|
|
case types::TY_Image:
|
|
llvm_unreachable("Output type can never be primary output.");
|
|
case types::TY_INVALID:
|
|
case types::TY_LAST:
|
|
llvm_unreachable("Invalid type ID");
|
|
}
|
|
break;
|
|
}
|
|
case OutputInfo::Mode::Immediate:
|
|
FrontendModeOption = "-i";
|
|
break;
|
|
case OutputInfo::Mode::REPL:
|
|
FrontendModeOption = "-repl";
|
|
break;
|
|
}
|
|
|
|
assert(FrontendModeOption != nullptr && "No frontend mode option specified!");
|
|
|
|
Arguments.push_back(FrontendModeOption);
|
|
|
|
assert(Inputs->empty() &&
|
|
"The Swift frontend does not expect to be fed any input Jobs!");
|
|
|
|
// Add input arguments.
|
|
switch (OI.CompilerMode) {
|
|
case OutputInfo::Mode::StandardCompile: {
|
|
assert(InputActions.size() == 1 &&
|
|
"The Swift frontend expects exactly one input (the primary file)!");
|
|
|
|
const InputAction *IA = dyn_cast<InputAction>(InputActions[0]);
|
|
assert(IA && "Only InputActions can be passed as inputs!");
|
|
const Arg &PrimaryInputArg = IA->getInputArg();
|
|
bool FoundPrimaryInput = false;
|
|
|
|
for (const Arg *A : make_range(Args.filtered_begin(options::OPT_INPUT),
|
|
Args.filtered_end())) {
|
|
Option Opt = A->getOption();
|
|
if (A->getOption().matches(options::OPT_INPUT)) {
|
|
// See if this input should be passed with -primary-file.
|
|
if (!FoundPrimaryInput && PrimaryInputArg.getIndex() == A->getIndex()) {
|
|
Arguments.push_back("-primary-file");
|
|
FoundPrimaryInput = true;
|
|
}
|
|
Arguments.push_back(A->getValue());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case OutputInfo::Mode::SingleCompile:
|
|
case OutputInfo::Mode::Immediate: {
|
|
for (const Action *A : InputActions) {
|
|
const InputAction *IA = dyn_cast<InputAction>(A);
|
|
assert(IA && "Only InputActions can be passed as inputs!");
|
|
|
|
IA->getInputArg().render(Args, Arguments);
|
|
}
|
|
break;
|
|
}
|
|
case OutputInfo::Mode::REPL: {
|
|
assert(InputActions.empty() && "REPL mode accepts no inputs!");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Args.hasArg(options::OPT_parse_stdlib))
|
|
Arguments.push_back("-enable-objc-attr-requires-objc-module");
|
|
|
|
addCommonFrontendArgs(getToolChain(), OI, Output.get(), Args, Arguments);
|
|
|
|
Args.AddLastArg(Arguments, options::OPT_module_link_name);
|
|
|
|
Args.AddLastArg(Arguments, options::OPT_split_objc_selectors);
|
|
|
|
Args.AddLastArg(Arguments, options::OPT_ignore_overlays);
|
|
|
|
// Pass the optimization level down to the frontend.
|
|
Args.AddLastArg(Arguments, options::OPT_O_Group);
|
|
|
|
// Handle the CPU and its preferences.
|
|
if (auto arg = Args.getLastArg(options::OPT_target_cpu)) {
|
|
arg->render(Args, Arguments);
|
|
} else {
|
|
configureDefaultCPU(getToolChain().getTriple(), Arguments);
|
|
}
|
|
Args.AddAllArgs(Arguments, options::OPT_target_feature);
|
|
|
|
if (Args.hasArg(options::OPT_parse_as_library) ||
|
|
Args.hasArg(options::OPT_emit_library))
|
|
Arguments.push_back("-parse-as-library");
|
|
|
|
Args.AddLastArg(Arguments, options::OPT_parse_sil);
|
|
|
|
Args.AddLastArg(Arguments, options::OPT_parse_stdlib);
|
|
|
|
Args.AddAllArgs(Arguments, options::OPT_l, options::OPT_framework);
|
|
|
|
const std::string &ModuleOutputPath =
|
|
Output->getAdditionalOutputForType(types::ID::TY_SwiftModuleFile);
|
|
if (!ModuleOutputPath.empty()) {
|
|
Arguments.push_back("-emit-module");
|
|
Arguments.push_back("-emit-module-path");
|
|
Arguments.push_back(ModuleOutputPath.c_str());
|
|
}
|
|
|
|
const std::string &SerializedDiagnosticsPath =
|
|
Output->getAdditionalOutputForType(types::TY_SerializedDiagnostics);
|
|
if (!SerializedDiagnosticsPath.empty()) {
|
|
Arguments.push_back("-serialize-diagnostics");
|
|
Arguments.push_back("-serialize-diagnostics-path");
|
|
Arguments.push_back(SerializedDiagnosticsPath.c_str());
|
|
}
|
|
|
|
// Pass on any build config options
|
|
for (const Arg *A : make_range(Args.filtered_begin(options::OPT_D),
|
|
Args.filtered_end())) {
|
|
Arguments.push_back("-D");
|
|
Arguments.push_back(A->getValue());
|
|
}
|
|
|
|
// Add the output file argument if necessary.
|
|
if (Output->getPrimaryOutputType() != types::TY_Nothing) {
|
|
Arguments.push_back("-o");
|
|
Arguments.push_back(Output->getPrimaryOutputFilename().c_str());
|
|
}
|
|
|
|
if (OI.CompilerMode == OutputInfo::Mode::Immediate)
|
|
Args.AddLastArg(Arguments, options::OPT__DASH_DASH);
|
|
|
|
return new Command(JA, *this, std::move(Inputs), std::move(Output), Exec,
|
|
Arguments);
|
|
}
|
|
|
|
|
|
Job *MergeModule::constructJob(const JobAction &JA,
|
|
std::unique_ptr<JobList> Inputs,
|
|
std::unique_ptr<CommandOutput> Output,
|
|
const ActionList &InputActions,
|
|
const ArgList &Args,
|
|
const OutputInfo &OI) const {
|
|
ArgStringList Arguments;
|
|
|
|
const char *Exec = getToolChain().getDriver().getSwiftProgramPath().c_str();
|
|
|
|
// Invoke ourself in -frontend mode.
|
|
Arguments.push_back("-frontend");
|
|
|
|
// We just want to emit a module, so pass -emit-module without any other
|
|
// mode options.
|
|
Arguments.push_back("-emit-module");
|
|
|
|
size_t origLen = Arguments.size();
|
|
(void)origLen;
|
|
addInputsOfType(Arguments, Inputs.get(), types::TY_SwiftModuleFile);
|
|
addInputsOfType(Arguments, InputActions, types::TY_SwiftModuleFile);
|
|
assert(Arguments.size() - origLen >= Inputs->size() + InputActions.size());
|
|
assert((Arguments.size() - origLen == Inputs->size() ||
|
|
!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");
|
|
|
|
addCommonFrontendArgs(getToolChain(), OI, Output.get(), Args, Arguments);
|
|
|
|
assert(Output->getPrimaryOutputType() == types::TY_SwiftModuleFile &&
|
|
"The MergeModule tool only produces swiftmodule files!");
|
|
|
|
const std::string &ObjCHeaderOutputPath =
|
|
Output->getAdditionalOutputForType(types::TY_ObjCHeader);
|
|
if (!ObjCHeaderOutputPath.empty()) {
|
|
Arguments.push_back("-emit-objc-header");
|
|
Arguments.push_back("-emit-objc-header-path");
|
|
Arguments.push_back(ObjCHeaderOutputPath.c_str());
|
|
}
|
|
|
|
Arguments.push_back("-o");
|
|
Arguments.push_back(Args.MakeArgString(Output->getPrimaryOutputFilename()));
|
|
|
|
return new Command(JA, *this, std::move(Inputs), std::move(Output), Exec,
|
|
Arguments);
|
|
}
|
|
|
|
/// Darwin Tools
|
|
|
|
llvm::Triple::ArchType darwin::getArchTypeForDarwinArchName(StringRef Arch) {
|
|
return llvm::StringSwitch<llvm::Triple::ArchType>(Arch)
|
|
.Cases("i386", "i486", "i486SX", "i586", "i686", llvm::Triple::x86)
|
|
.Cases("pentium", "pentpro", "pentIIm3", "pentIIm5", "pentium4",
|
|
llvm::Triple::x86)
|
|
|
|
.Case("x86_64", llvm::Triple::x86_64)
|
|
|
|
.Case("arm64", llvm::Triple::arm64)
|
|
|
|
.Cases("arm", "armv4t", "armv5", "armv6", "armv6m", llvm::Triple::arm)
|
|
.Cases("armv7", "armv7em", "armv7f", "armv7k", "armv7m", llvm::Triple::arm)
|
|
.Cases("armv7s", "xscale", llvm::Triple::arm)
|
|
|
|
.Default(llvm::Triple::UnknownArch);
|
|
}
|
|
|
|
void darwin::DarwinTool::anchor() {}
|
|
|
|
void darwin::DarwinTool::AddDarwinArch(const ArgList &Args,
|
|
ArgStringList &CmdArgs) const {
|
|
StringRef ArchName = getDarwinToolChain().getDarwinArchName(Args);
|
|
|
|
CmdArgs.push_back("-arch");
|
|
CmdArgs.push_back(Args.MakeArgString(ArchName));
|
|
}
|
|
|
|
Job *darwin::Linker::constructJob(const JobAction &JA,
|
|
std::unique_ptr<JobList> Inputs,
|
|
std::unique_ptr<CommandOutput> Output,
|
|
const ActionList &InputActions,
|
|
const ArgList &Args,
|
|
const OutputInfo &OI) const {
|
|
assert(Output->getPrimaryOutputType() == types::TY_Image &&
|
|
"Invalid linker output type.");
|
|
|
|
ArgStringList Arguments;
|
|
addPrimaryInputsOfType(Arguments, Inputs.get(), types::TY_Object);
|
|
addInputsOfType(Arguments, InputActions, types::TY_Object);
|
|
|
|
if (Args.hasArg(options::OPT_g)) {
|
|
Arguments.push_back("-sectalign");
|
|
Arguments.push_back(MachOASTSegmentName);
|
|
Arguments.push_back(MachOASTSectionName);
|
|
Arguments.push_back("4");
|
|
|
|
Arguments.push_back("-sectcreate");
|
|
Arguments.push_back(MachOASTSegmentName);
|
|
Arguments.push_back(MachOASTSectionName);
|
|
|
|
size_t argCount = Arguments.size();
|
|
if (OI.CompilerMode == OutputInfo::Mode::SingleCompile)
|
|
addInputsOfType(Arguments, Inputs.get(), types::TY_SwiftModuleFile);
|
|
else
|
|
addPrimaryInputsOfType(Arguments, Inputs.get(),
|
|
types::TY_SwiftModuleFile);
|
|
assert(argCount + 1 == Arguments.size() && "no swiftmodule found for -g");
|
|
(void)argCount;
|
|
}
|
|
|
|
switch (cast<LinkJobAction>(JA).getKind()) {
|
|
case LinkKind::None:
|
|
llvm_unreachable("invalid link kind");
|
|
case LinkKind::Executable:
|
|
// The default for ld; no extra flags necessary.
|
|
break;
|
|
case LinkKind::DynamicLibrary:
|
|
Arguments.push_back("-dylib");
|
|
break;
|
|
}
|
|
|
|
Args.AddAllArgValues(Arguments, options::OPT_Xlinker);
|
|
Args.AddAllArgs(Arguments, options::OPT_linker_option_Group);
|
|
Args.AddAllArgs(Arguments, options::OPT_F);
|
|
|
|
if (Args.hasArg(options::OPT_enable_app_extension)) {
|
|
// Keep this string fixed in case the option used by the
|
|
// compiler itself changes.
|
|
Arguments.push_back("-application_extension");
|
|
}
|
|
|
|
if (!OI.SDKPath.empty()) {
|
|
Arguments.push_back("-syslibroot");
|
|
Arguments.push_back(Args.MakeArgString(OI.SDKPath));
|
|
}
|
|
|
|
Arguments.push_back("-lSystem");
|
|
AddDarwinArch(Args, Arguments);
|
|
|
|
const toolchains::Darwin &TC = getDarwinToolChain();
|
|
const Driver &D = TC.getDriver();
|
|
|
|
// Add the runtime library link path, which is platform-specific and found
|
|
// relative to the compiler.
|
|
// FIXME: Duplicated from CompilerInvocation, but in theory the runtime
|
|
// library link path and the standard library module import path don't
|
|
// need to be the same.
|
|
llvm::SmallString<128> RuntimeLibPath;
|
|
|
|
if (const Arg *A = Args.getLastArg(options::OPT_resource_dir)) {
|
|
RuntimeLibPath = A->getValue();
|
|
} else {
|
|
RuntimeLibPath = D.getSwiftProgramPath();
|
|
llvm::sys::path::remove_filename(RuntimeLibPath); // remove /swift
|
|
llvm::sys::path::remove_filename(RuntimeLibPath); // remove /bin
|
|
llvm::sys::path::append(RuntimeLibPath, "lib", "swift");
|
|
}
|
|
llvm::sys::path::append(RuntimeLibPath,
|
|
getPlatformNameForTriple(TC.getTriple()));
|
|
Arguments.push_back("-L");
|
|
Arguments.push_back(Args.MakeArgString(RuntimeLibPath));
|
|
|
|
// FIXME: We probably shouldn't be adding an rpath here unless we know ahead
|
|
// of time the standard library won't be copied.
|
|
Arguments.push_back("-rpath");
|
|
Arguments.push_back(Args.MakeArgString(RuntimeLibPath));
|
|
|
|
// FIXME: Properly handle deployment targets.
|
|
llvm::Triple Triple = TC.getTriple();
|
|
assert(Triple.isiOS() || Triple.isMacOSX());
|
|
if (Triple.isiOS()) {
|
|
if (tripleIsiOSSimulator(Triple))
|
|
Arguments.push_back("-ios_simulator_version_min");
|
|
else
|
|
Arguments.push_back("-iphoneos_version_min");
|
|
Arguments.push_back("7.0.0");
|
|
} else {
|
|
Arguments.push_back("-macosx_version_min");
|
|
Arguments.push_back("10.8.0");
|
|
}
|
|
|
|
Arguments.push_back("-no_objc_category_merging");
|
|
|
|
// This should be the last option, for convenience in checking output.
|
|
Arguments.push_back("-o");
|
|
Arguments.push_back(Output->getPrimaryOutputFilename().c_str());
|
|
|
|
std::string Exec = D.getProgramPath("ld", getToolChain());
|
|
|
|
return new Command(JA, *this, std::move(Inputs), std::move(Output),
|
|
Args.MakeArgString(Exec), Arguments);
|
|
}
|