Files
swift-mirror/lib/Driver/WebAssemblyToolChains.cpp
Saleem Abdulrasool ef39af0f55 Driver: support -nostartfiles in the C++ driver
This is required to bootstrap the `-static-stdlib` support for Windows.
With this, we are able to properly build the Swift SDK both dynamically
and statically, which is needed to enable us to make further progress
towards an early swift-driver.
2025-02-07 17:40:26 -08:00

252 lines
9.2 KiB
C++

//===---- WebAssemblyToolChains.cpp - Job invocations (WebAssembly-specific) ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 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/ABI/System.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/Config.h"
#include "swift/Driver/Compilation.h"
#include "swift/Driver/Driver.h"
#include "swift/Driver/Job.h"
#include "swift/Option/Options.h"
#include "swift/Option/SanitizerOptions.h"
#include "clang/Basic/Version.h"
#include "clang/Driver/Util.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;
std::string
toolchains::WebAssembly::sanitizerRuntimeLibName(StringRef Sanitizer,
bool shared) const {
return (Twine("clang_rt.") + Sanitizer + "-" +
this->getTriple().getArchName() + ".lib")
.str();
}
ToolChain::InvocationInfo toolchains::WebAssembly::constructInvocation(
const AutolinkExtractJobAction &job, const JobContext &context) const {
assert(context.Output.getPrimaryOutputType() == file_types::TY_AutolinkFile);
InvocationInfo II{"swift-autolink-extract"};
ArgStringList &Arguments = II.Arguments;
II.allowsResponseFiles = true;
addPrimaryInputsOfType(Arguments, context.Inputs, context.Args,
file_types::TY_Object);
addInputsOfType(Arguments, context.InputActions, file_types::TY_Object);
Arguments.push_back("-o");
Arguments.push_back(
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
return II;
}
ToolChain::InvocationInfo
toolchains::WebAssembly::constructInvocation(const DynamicLinkJobAction &job,
const JobContext &context) const {
assert(context.Output.getPrimaryOutputType() == file_types::TY_Image &&
"Invalid linker output type.");
ArgStringList Arguments;
std::string Target = getTriple().str();
if (!Target.empty()) {
Arguments.push_back("-target");
Arguments.push_back(context.Args.MakeArgString(Target));
}
switch (job.getKind()) {
case LinkKind::None:
llvm_unreachable("invalid link kind");
case LinkKind::Executable:
// Default case, nothing extra needed.
break;
case LinkKind::DynamicLibrary:
llvm_unreachable("WebAssembly doesn't support dynamic library yet");
case LinkKind::StaticLibrary:
llvm_unreachable("invalid link kind");
}
// Select the linker to use.
std::string Linker;
if (const Arg *A = context.Args.getLastArg(options::OPT_use_ld)) {
Linker = A->getValue();
}
if (!Linker.empty())
Arguments.push_back(context.Args.MakeArgString("-fuse-ld=" + Linker));
const char *Clang = "clang";
if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) {
StringRef toolchainPath(A->getValue());
// If there is a clang in the toolchain folder, use that instead.
if (auto tool = llvm::sys::findProgramByName("clang", {toolchainPath}))
Clang = context.Args.MakeArgString(tool.get());
}
SmallVector<std::string, 4> RuntimeLibPaths;
getRuntimeLibraryPaths(RuntimeLibPaths, context.Args, context.OI.SDKPath,
/*Shared=*/false);
SmallString<128> SharedResourceDirPath;
getResourceDirPath(SharedResourceDirPath, context.Args, /*Shared=*/false);
if (!context.Args.hasArg(options::OPT_nostartfiles)) {
SmallString<128> swiftrtPath = SharedResourceDirPath;
llvm::sys::path::append(swiftrtPath,
swift::getMajorArchitectureName(getTriple()));
llvm::sys::path::append(swiftrtPath, "swiftrt.o");
Arguments.push_back(context.Args.MakeArgString(swiftrtPath));
}
addPrimaryInputsOfType(Arguments, context.Inputs, context.Args,
file_types::TY_Object);
addInputsOfType(Arguments, context.InputActions, file_types::TY_Object);
addInputsOfType(Arguments, context.InputActions, file_types::TY_LLVM_BC);
if (!context.OI.SDKPath.empty()) {
Arguments.push_back("--sysroot");
Arguments.push_back(context.Args.MakeArgString(context.OI.SDKPath));
}
// Add any autolinking scripts to the arguments
for (const Job *Cmd : context.Inputs) {
auto &OutputInfo = Cmd->getOutput();
if (OutputInfo.getPrimaryOutputType() == file_types::TY_AutolinkFile)
Arguments.push_back(context.Args.MakeArgString(
Twine("@") + OutputInfo.getPrimaryOutputFilename()));
}
// Add the runtime library link paths.
for (auto path : RuntimeLibPaths) {
Arguments.push_back("-L");
Arguments.push_back(context.Args.MakeArgString(path));
}
// Link the standard library. In two paths, we do this using a .lnk file;
// if we're going that route, we'll set `linkFilePath` to the path to that
// file.
SmallString<128> linkFilePath;
getResourceDirPath(linkFilePath, context.Args, /*Shared=*/false);
llvm::sys::path::append(linkFilePath, "static-executable-args.lnk");
auto linkFile = linkFilePath.str();
if (llvm::sys::fs::is_regular_file(linkFile)) {
Arguments.push_back(context.Args.MakeArgString(Twine("@") + linkFile));
} else {
llvm::report_fatal_error(linkFile + " not found");
}
// Delegate to Clang for sanitizers. It will figure out the correct linker
// options.
if (job.getKind() == LinkKind::Executable && context.OI.SelectedSanitizers) {
Arguments.push_back(context.Args.MakeArgString(
"-fsanitize=" + getSanitizerList(context.OI.SelectedSanitizers)));
}
if (context.Args.hasArg(options::OPT_profile_generate)) {
SmallString<128> LibProfile(SharedResourceDirPath);
llvm::sys::path::remove_filename(LibProfile); // remove platform name
llvm::sys::path::append(LibProfile, "clang", "lib");
llvm::sys::path::append(LibProfile, getTriple().getOSName(),
Twine("libclang_rt.profile-") +
getTriple().getArchName() + ".a");
Arguments.push_back(context.Args.MakeArgString(LibProfile));
Arguments.push_back(context.Args.MakeArgString(
Twine("-u", llvm::getInstrProfRuntimeHookVarName())));
}
// Run clang++ in verbose mode if "-v" is set
if (context.Args.hasArg(options::OPT_v)) {
Arguments.push_back("-v");
}
// WebAssembly doesn't reserve low addresses But without "extra inhabitants"
// of the pointer representation, runtime performance and memory footprint are
// worse. So assume that compiler driver uses wasm-ld and --global-base=1024
// to reserve low 1KB.
Arguments.push_back("-Xlinker");
Arguments.push_back(context.Args.MakeArgString(
Twine("--global-base=") +
std::to_string(SWIFT_ABI_WASM32_LEAST_VALID_POINTER)));
// These custom arguments should be right before the object file at the end.
context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group);
context.Args.AddAllArgs(Arguments, options::OPT_Xlinker);
context.Args.AddAllArgValues(Arguments, options::OPT_Xclang_linker);
// This should be the last option, for convenience in checking output.
Arguments.push_back("-o");
Arguments.push_back(
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
InvocationInfo II{Clang, Arguments};
II.allowsResponseFiles = true;
return II;
}
void validateLinkerArguments(DiagnosticEngine &diags,
ArgStringList linkerArgs) {
for (auto arg : linkerArgs) {
if (StringRef(arg).starts_with("--global-base=")) {
diags.diagnose(SourceLoc(), diag::error_wasm_doesnt_support_global_base);
}
}
}
void toolchains::WebAssembly::validateArguments(DiagnosticEngine &diags,
const llvm::opt::ArgList &args,
StringRef defaultTarget) const {
ArgStringList linkerArgs;
args.AddAllArgValues(linkerArgs, options::OPT_Xlinker);
validateLinkerArguments(diags, linkerArgs);
}
ToolChain::InvocationInfo
toolchains::WebAssembly::constructInvocation(const StaticLinkJobAction &job,
const JobContext &context) const {
assert(context.Output.getPrimaryOutputType() == file_types::TY_Image &&
"Invalid linker output type.");
ArgStringList Arguments;
const char *AR = "llvm-ar";
Arguments.push_back("crs");
Arguments.push_back(
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
addPrimaryInputsOfType(Arguments, context.Inputs, context.Args,
file_types::TY_Object);
addInputsOfType(Arguments, context.InputActions, file_types::TY_Object);
InvocationInfo II{AR, Arguments};
return II;
}