Skip to content

Driver: add support for the WebAssembly toolchain #39299

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/DiagnosticsDriver.def
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ ERROR(error_darwin_only_supports_libcxx, none,
"The only C++ standard library supported on Apple platforms is libc++",
())

ERROR(error_wasm_doesnt_support_global_base, none,
"The --global-base argument is not supported when targeting WebAssembly",
())

ERROR(error_hermetic_seal_cannot_have_library_evolution, none,
"Cannot use -experimental-hermetic-seal-at-link with -enable-library-evolution",
())
Expand Down
1 change: 1 addition & 0 deletions lib/Driver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ set(swiftDriver_sources
ToolChain.cpp
ToolChains.cpp
UnixToolChains.cpp
WebAssemblyToolChains.cpp
WindowsToolChains.cpp
)

Expand Down
20 changes: 20 additions & 0 deletions lib/Driver/ToolChains.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,26 @@ class LLVM_LIBRARY_VISIBILITY Windows : public ToolChain {
bool shared = true) const override;
};

class LLVM_LIBRARY_VISIBILITY WebAssembly : public ToolChain {
protected:
InvocationInfo constructInvocation(const AutolinkExtractJobAction &job,
const JobContext &context) const override;
InvocationInfo constructInvocation(const DynamicLinkJobAction &job,
const JobContext &context) const override;
InvocationInfo constructInvocation(const StaticLinkJobAction &job,
const JobContext &context) const override;
void validateArguments(DiagnosticEngine &diags,
const llvm::opt::ArgList &args,
StringRef defaultTarget) const override;

public:
WebAssembly(const Driver &D, const llvm::Triple &Triple) : ToolChain(D, Triple) {}
~WebAssembly() = default;
std::string sanitizerRuntimeLibName(StringRef Sanitizer,
bool shared = true) const override;
};


class LLVM_LIBRARY_VISIBILITY GenericUnix : public ToolChain {
protected:
InvocationInfo constructInvocation(const InterpretJobAction &job,
Expand Down
246 changes: 246 additions & 0 deletions lib/Driver/WebAssemblyToolChains.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
//===---- 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/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);

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");

// 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).startswith("--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;
}
4 changes: 4 additions & 0 deletions test/Driver/wasm.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// RUN: %swiftc_driver -driver-print-jobs -target wasm32-unknown-wasi -v %s 2>&1 | %FileCheck %s -check-prefix=CHECK-WASM

// CHECK-WASM: swift{{.*}} -frontend -c -primary-file {{.*}} -target wasm32-unknown-wasi -disable-objc-interop
// CHECK-WASM: clang{{.*}} -lswiftCore --target=wasm32-unknown-wasi -v {{.*}}-o