Skip to content

Commit 19ea9e7

Browse files
authored
Merge pull request #39299 from apple/maxd/driver-wasm-toolchain
2 parents 698e06c + e738a5d commit 19ea9e7

File tree

5 files changed

+275
-0
lines changed

5 files changed

+275
-0
lines changed

include/swift/AST/DiagnosticsDriver.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ ERROR(error_darwin_only_supports_libcxx, none,
166166
"The only C++ standard library supported on Apple platforms is libc++",
167167
())
168168

169+
ERROR(error_wasm_doesnt_support_global_base, none,
170+
"The --global-base argument is not supported when targeting WebAssembly",
171+
())
172+
169173
ERROR(error_hermetic_seal_cannot_have_library_evolution, none,
170174
"Cannot use -experimental-hermetic-seal-at-link with -enable-library-evolution",
171175
())

lib/Driver/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ set(swiftDriver_sources
1212
ToolChain.cpp
1313
ToolChains.cpp
1414
UnixToolChains.cpp
15+
WebAssemblyToolChains.cpp
1516
WindowsToolChains.cpp
1617
)
1718

lib/Driver/ToolChains.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,26 @@ class LLVM_LIBRARY_VISIBILITY Windows : public ToolChain {
113113
bool shared = true) const override;
114114
};
115115

116+
class LLVM_LIBRARY_VISIBILITY WebAssembly : public ToolChain {
117+
protected:
118+
InvocationInfo constructInvocation(const AutolinkExtractJobAction &job,
119+
const JobContext &context) const override;
120+
InvocationInfo constructInvocation(const DynamicLinkJobAction &job,
121+
const JobContext &context) const override;
122+
InvocationInfo constructInvocation(const StaticLinkJobAction &job,
123+
const JobContext &context) const override;
124+
void validateArguments(DiagnosticEngine &diags,
125+
const llvm::opt::ArgList &args,
126+
StringRef defaultTarget) const override;
127+
128+
public:
129+
WebAssembly(const Driver &D, const llvm::Triple &Triple) : ToolChain(D, Triple) {}
130+
~WebAssembly() = default;
131+
std::string sanitizerRuntimeLibName(StringRef Sanitizer,
132+
bool shared = true) const override;
133+
};
134+
135+
116136
class LLVM_LIBRARY_VISIBILITY GenericUnix : public ToolChain {
117137
protected:
118138
InvocationInfo constructInvocation(const InterpretJobAction &job,

lib/Driver/WebAssemblyToolChains.cpp

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
//===---- WebAssemblyToolChains.cpp - Job invocations (WebAssembly-specific)
2+
//------===//
3+
//
4+
// This source file is part of the Swift.org open source project
5+
//
6+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
7+
// Licensed under Apache License v2.0 with Runtime Library Exception
8+
//
9+
// See https://swift.org/LICENSE.txt for license information
10+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "ToolChains.h"
15+
16+
#include "swift/ABI/System.h"
17+
#include "swift/AST/DiagnosticsDriver.h"
18+
#include "swift/Basic/LLVM.h"
19+
#include "swift/Basic/Platform.h"
20+
#include "swift/Basic/Range.h"
21+
#include "swift/Config.h"
22+
#include "swift/Driver/Compilation.h"
23+
#include "swift/Driver/Driver.h"
24+
#include "swift/Driver/Job.h"
25+
#include "swift/Option/Options.h"
26+
#include "swift/Option/SanitizerOptions.h"
27+
#include "clang/Basic/Version.h"
28+
#include "clang/Driver/Util.h"
29+
#include "llvm/ADT/StringSwitch.h"
30+
#include "llvm/Option/Arg.h"
31+
#include "llvm/Option/ArgList.h"
32+
#include "llvm/ProfileData/InstrProf.h"
33+
#include "llvm/Support/FileSystem.h"
34+
#include "llvm/Support/Path.h"
35+
#include "llvm/Support/Process.h"
36+
#include "llvm/Support/Program.h"
37+
38+
using namespace swift;
39+
using namespace swift::driver;
40+
using namespace llvm::opt;
41+
42+
std::string
43+
toolchains::WebAssembly::sanitizerRuntimeLibName(StringRef Sanitizer,
44+
bool shared) const {
45+
return (Twine("clang_rt.") + Sanitizer + "-" +
46+
this->getTriple().getArchName() + ".lib")
47+
.str();
48+
}
49+
50+
ToolChain::InvocationInfo toolchains::WebAssembly::constructInvocation(
51+
const AutolinkExtractJobAction &job, const JobContext &context) const {
52+
assert(context.Output.getPrimaryOutputType() == file_types::TY_AutolinkFile);
53+
54+
InvocationInfo II{"swift-autolink-extract"};
55+
ArgStringList &Arguments = II.Arguments;
56+
II.allowsResponseFiles = true;
57+
58+
addPrimaryInputsOfType(Arguments, context.Inputs, context.Args,
59+
file_types::TY_Object);
60+
addInputsOfType(Arguments, context.InputActions, file_types::TY_Object);
61+
62+
Arguments.push_back("-o");
63+
Arguments.push_back(
64+
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
65+
66+
return II;
67+
}
68+
69+
ToolChain::InvocationInfo
70+
toolchains::WebAssembly::constructInvocation(const DynamicLinkJobAction &job,
71+
const JobContext &context) const {
72+
assert(context.Output.getPrimaryOutputType() == file_types::TY_Image &&
73+
"Invalid linker output type.");
74+
75+
ArgStringList Arguments;
76+
77+
std::string Target = getTriple().str();
78+
if (!Target.empty()) {
79+
Arguments.push_back("-target");
80+
Arguments.push_back(context.Args.MakeArgString(Target));
81+
}
82+
83+
switch (job.getKind()) {
84+
case LinkKind::None:
85+
llvm_unreachable("invalid link kind");
86+
case LinkKind::Executable:
87+
// Default case, nothing extra needed.
88+
break;
89+
case LinkKind::DynamicLibrary:
90+
llvm_unreachable("WebAssembly doesn't support dynamic library yet");
91+
case LinkKind::StaticLibrary:
92+
llvm_unreachable("invalid link kind");
93+
}
94+
95+
// Select the linker to use.
96+
std::string Linker;
97+
if (const Arg *A = context.Args.getLastArg(options::OPT_use_ld)) {
98+
Linker = A->getValue();
99+
}
100+
if (!Linker.empty())
101+
Arguments.push_back(context.Args.MakeArgString("-fuse-ld=" + Linker));
102+
103+
const char *Clang = "clang";
104+
if (const Arg *A = context.Args.getLastArg(options::OPT_tools_directory)) {
105+
StringRef toolchainPath(A->getValue());
106+
107+
// If there is a clang in the toolchain folder, use that instead.
108+
if (auto tool = llvm::sys::findProgramByName("clang", {toolchainPath}))
109+
Clang = context.Args.MakeArgString(tool.get());
110+
}
111+
112+
SmallVector<std::string, 4> RuntimeLibPaths;
113+
getRuntimeLibraryPaths(RuntimeLibPaths, context.Args, context.OI.SDKPath,
114+
/*Shared=*/false);
115+
116+
SmallString<128> SharedResourceDirPath;
117+
getResourceDirPath(SharedResourceDirPath, context.Args, /*Shared=*/false);
118+
119+
SmallString<128> swiftrtPath = SharedResourceDirPath;
120+
llvm::sys::path::append(swiftrtPath,
121+
swift::getMajorArchitectureName(getTriple()));
122+
llvm::sys::path::append(swiftrtPath, "swiftrt.o");
123+
Arguments.push_back(context.Args.MakeArgString(swiftrtPath));
124+
125+
addPrimaryInputsOfType(Arguments, context.Inputs, context.Args,
126+
file_types::TY_Object);
127+
addInputsOfType(Arguments, context.InputActions, file_types::TY_Object);
128+
addInputsOfType(Arguments, context.InputActions, file_types::TY_LLVM_BC);
129+
130+
if (!context.OI.SDKPath.empty()) {
131+
Arguments.push_back("--sysroot");
132+
Arguments.push_back(context.Args.MakeArgString(context.OI.SDKPath));
133+
}
134+
135+
// Add any autolinking scripts to the arguments
136+
for (const Job *Cmd : context.Inputs) {
137+
auto &OutputInfo = Cmd->getOutput();
138+
if (OutputInfo.getPrimaryOutputType() == file_types::TY_AutolinkFile)
139+
Arguments.push_back(context.Args.MakeArgString(
140+
Twine("@") + OutputInfo.getPrimaryOutputFilename()));
141+
}
142+
143+
// Add the runtime library link paths.
144+
for (auto path : RuntimeLibPaths) {
145+
Arguments.push_back("-L");
146+
Arguments.push_back(context.Args.MakeArgString(path));
147+
}
148+
// Link the standard library. In two paths, we do this using a .lnk file;
149+
// if we're going that route, we'll set `linkFilePath` to the path to that
150+
// file.
151+
SmallString<128> linkFilePath;
152+
getResourceDirPath(linkFilePath, context.Args, /*Shared=*/false);
153+
llvm::sys::path::append(linkFilePath, "static-executable-args.lnk");
154+
155+
auto linkFile = linkFilePath.str();
156+
if (llvm::sys::fs::is_regular_file(linkFile)) {
157+
Arguments.push_back(context.Args.MakeArgString(Twine("@") + linkFile));
158+
} else {
159+
llvm::report_fatal_error(linkFile + " not found");
160+
}
161+
162+
// Delegate to Clang for sanitizers. It will figure out the correct linker
163+
// options.
164+
if (job.getKind() == LinkKind::Executable && context.OI.SelectedSanitizers) {
165+
Arguments.push_back(context.Args.MakeArgString(
166+
"-fsanitize=" + getSanitizerList(context.OI.SelectedSanitizers)));
167+
}
168+
169+
if (context.Args.hasArg(options::OPT_profile_generate)) {
170+
SmallString<128> LibProfile(SharedResourceDirPath);
171+
llvm::sys::path::remove_filename(LibProfile); // remove platform name
172+
llvm::sys::path::append(LibProfile, "clang", "lib");
173+
174+
llvm::sys::path::append(LibProfile, getTriple().getOSName(),
175+
Twine("libclang_rt.profile-") +
176+
getTriple().getArchName() + ".a");
177+
Arguments.push_back(context.Args.MakeArgString(LibProfile));
178+
Arguments.push_back(context.Args.MakeArgString(
179+
Twine("-u", llvm::getInstrProfRuntimeHookVarName())));
180+
}
181+
182+
// Run clang++ in verbose mode if "-v" is set
183+
if (context.Args.hasArg(options::OPT_v)) {
184+
Arguments.push_back("-v");
185+
}
186+
187+
// WebAssembly doesn't reserve low addresses But without "extra inhabitants"
188+
// of the pointer representation, runtime performance and memory footprint are
189+
// worse. So assume that compiler driver uses wasm-ld and --global-base=1024
190+
// to reserve low 1KB.
191+
Arguments.push_back("-Xlinker");
192+
193+
// These custom arguments should be right before the object file at the end.
194+
context.Args.AddAllArgs(Arguments, options::OPT_linker_option_Group);
195+
context.Args.AddAllArgs(Arguments, options::OPT_Xlinker);
196+
context.Args.AddAllArgValues(Arguments, options::OPT_Xclang_linker);
197+
198+
// This should be the last option, for convenience in checking output.
199+
Arguments.push_back("-o");
200+
Arguments.push_back(
201+
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
202+
203+
InvocationInfo II{Clang, Arguments};
204+
II.allowsResponseFiles = true;
205+
206+
return II;
207+
}
208+
209+
void validateLinkerArguments(DiagnosticEngine &diags,
210+
ArgStringList linkerArgs) {
211+
for (auto arg : linkerArgs) {
212+
if (StringRef(arg).startswith("--global-base=")) {
213+
diags.diagnose(SourceLoc(), diag::error_wasm_doesnt_support_global_base);
214+
}
215+
}
216+
}
217+
void toolchains::WebAssembly::validateArguments(DiagnosticEngine &diags,
218+
const llvm::opt::ArgList &args,
219+
StringRef defaultTarget) const {
220+
ArgStringList linkerArgs;
221+
args.AddAllArgValues(linkerArgs, options::OPT_Xlinker);
222+
validateLinkerArguments(diags, linkerArgs);
223+
}
224+
225+
ToolChain::InvocationInfo
226+
toolchains::WebAssembly::constructInvocation(const StaticLinkJobAction &job,
227+
const JobContext &context) const {
228+
assert(context.Output.getPrimaryOutputType() == file_types::TY_Image &&
229+
"Invalid linker output type.");
230+
231+
ArgStringList Arguments;
232+
233+
const char *AR = "llvm-ar";
234+
Arguments.push_back("crs");
235+
236+
Arguments.push_back(
237+
context.Args.MakeArgString(context.Output.getPrimaryOutputFilename()));
238+
239+
addPrimaryInputsOfType(Arguments, context.Inputs, context.Args,
240+
file_types::TY_Object);
241+
addInputsOfType(Arguments, context.InputActions, file_types::TY_Object);
242+
243+
InvocationInfo II{AR, Arguments};
244+
245+
return II;
246+
}

test/Driver/wasm.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// RUN: %swiftc_driver -driver-print-jobs -target wasm32-unknown-wasi -v %s 2>&1 | %FileCheck %s -check-prefix=CHECK-WASM
2+
3+
// CHECK-WASM: swift{{.*}} -frontend -c -primary-file {{.*}} -target wasm32-unknown-wasi -disable-objc-interop
4+
// CHECK-WASM: clang{{.*}} -lswiftCore --target=wasm32-unknown-wasi -v {{.*}}-o

0 commit comments

Comments
 (0)