1 //===--- WebAssembly.cpp - WebAssembly ToolChain Implementation -*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "WebAssembly.h"
10 #include "CommonArgs.h"
12 #include "clang/Basic/Version.h"
13 #include "clang/Config/config.h"
14 #include "clang/Driver/Compilation.h"
15 #include "clang/Driver/Driver.h"
16 #include "clang/Driver/DriverDiagnostic.h"
17 #include "clang/Driver/Options.h"
18 #include "llvm/Option/ArgList.h"
19 #include "llvm/Support/FileSystem.h"
20 #include "llvm/Support/Path.h"
21 #include "llvm/Support/VirtualFileSystem.h"
23 using namespace clang::driver
;
24 using namespace clang::driver::tools
;
25 using namespace clang::driver::toolchains
;
26 using namespace clang
;
27 using namespace llvm::opt
;
29 /// Following the conventions in https://wiki.debian.org/Multiarch/Tuples,
30 /// we remove the vendor field to form the multiarch triple.
31 std::string
WebAssembly::getMultiarchTriple(const Driver
&D
,
32 const llvm::Triple
&TargetTriple
,
33 StringRef SysRoot
) const {
34 return (TargetTriple
.getArchName() + "-" +
35 TargetTriple
.getOSAndEnvironmentName()).str();
38 std::string
wasm::Linker::getLinkerPath(const ArgList
&Args
) const {
39 const ToolChain
&ToolChain
= getToolChain();
40 if (const Arg
* A
= Args
.getLastArg(options::OPT_fuse_ld_EQ
)) {
41 StringRef UseLinker
= A
->getValue();
42 if (!UseLinker
.empty()) {
43 if (llvm::sys::path::is_absolute(UseLinker
) &&
44 llvm::sys::fs::can_execute(UseLinker
))
45 return std::string(UseLinker
);
47 // Accept 'lld', and 'ld' as aliases for the default linker
48 if (UseLinker
!= "lld" && UseLinker
!= "ld")
49 ToolChain
.getDriver().Diag(diag::err_drv_invalid_linker_name
)
50 << A
->getAsString(Args
);
54 return ToolChain
.GetProgramPath(ToolChain
.getDefaultLinker());
57 void wasm::Linker::ConstructJob(Compilation
&C
, const JobAction
&JA
,
58 const InputInfo
&Output
,
59 const InputInfoList
&Inputs
,
61 const char *LinkingOutput
) const {
63 const ToolChain
&ToolChain
= getToolChain();
64 const char *Linker
= Args
.MakeArgString(getLinkerPath(Args
));
65 ArgStringList CmdArgs
;
67 CmdArgs
.push_back("-m");
68 if (ToolChain
.getTriple().isArch64Bit())
69 CmdArgs
.push_back("wasm64");
71 CmdArgs
.push_back("wasm32");
73 if (Args
.hasArg(options::OPT_s
))
74 CmdArgs
.push_back("--strip-all");
76 Args
.AddAllArgs(CmdArgs
, options::OPT_L
);
77 Args
.AddAllArgs(CmdArgs
, options::OPT_u
);
78 ToolChain
.AddFilePathLibArgs(Args
, CmdArgs
);
80 const char *Crt1
= "crt1.o";
81 const char *Entry
= nullptr;
83 // If crt1-command.o exists, it supports new-style commands, so use it.
84 // Otherwise, use the old crt1.o. This is a temporary transition measure.
85 // Once WASI libc no longer needs to support LLVM versions which lack
86 // support for new-style command, it can make crt1.o the same as
87 // crt1-command.o. And once LLVM no longer needs to support WASI libc
88 // versions before that, it can switch to using crt1-command.o.
89 if (ToolChain
.GetFilePath("crt1-command.o") != "crt1-command.o")
90 Crt1
= "crt1-command.o";
92 if (const Arg
*A
= Args
.getLastArg(options::OPT_mexec_model_EQ
)) {
93 StringRef CM
= A
->getValue();
94 if (CM
== "command") {
95 // Use default values.
96 } else if (CM
== "reactor") {
97 Crt1
= "crt1-reactor.o";
98 Entry
= "_initialize";
100 ToolChain
.getDriver().Diag(diag::err_drv_invalid_argument_to_option
)
101 << CM
<< A
->getOption().getName();
104 if (!Args
.hasArg(options::OPT_nostdlib
, options::OPT_nostartfiles
))
105 CmdArgs
.push_back(Args
.MakeArgString(ToolChain
.GetFilePath(Crt1
)));
107 CmdArgs
.push_back(Args
.MakeArgString("--entry"));
108 CmdArgs
.push_back(Args
.MakeArgString(Entry
));
111 AddLinkerInputs(ToolChain
, Inputs
, Args
, CmdArgs
, JA
);
113 if (!Args
.hasArg(options::OPT_nostdlib
, options::OPT_nodefaultlibs
)) {
114 if (ToolChain
.ShouldLinkCXXStdlib(Args
))
115 ToolChain
.AddCXXStdlibLibArgs(Args
, CmdArgs
);
117 if (Args
.hasArg(options::OPT_pthread
)) {
118 CmdArgs
.push_back("-lpthread");
119 CmdArgs
.push_back("--shared-memory");
122 CmdArgs
.push_back("-lc");
123 AddRunTimeLibs(ToolChain
, ToolChain
.getDriver(), CmdArgs
, Args
);
126 CmdArgs
.push_back("-o");
127 CmdArgs
.push_back(Output
.getFilename());
129 C
.addCommand(std::make_unique
<Command
>(JA
, *this,
130 ResponseFileSupport::AtFileCurCP(),
131 Linker
, CmdArgs
, Inputs
, Output
));
133 // When optimizing, if wasm-opt is available, run it.
134 if (Arg
*A
= Args
.getLastArg(options::OPT_O_Group
)) {
135 auto WasmOptPath
= ToolChain
.GetProgramPath("wasm-opt");
136 if (WasmOptPath
!= "wasm-opt") {
137 StringRef OOpt
= "s";
138 if (A
->getOption().matches(options::OPT_O4
) ||
139 A
->getOption().matches(options::OPT_Ofast
))
141 else if (A
->getOption().matches(options::OPT_O0
))
143 else if (A
->getOption().matches(options::OPT_O
))
144 OOpt
= A
->getValue();
147 const char *WasmOpt
= Args
.MakeArgString(WasmOptPath
);
148 ArgStringList CmdArgs
;
149 CmdArgs
.push_back(Output
.getFilename());
150 CmdArgs
.push_back(Args
.MakeArgString(llvm::Twine("-O") + OOpt
));
151 CmdArgs
.push_back("-o");
152 CmdArgs
.push_back(Output
.getFilename());
153 C
.addCommand(std::make_unique
<Command
>(
154 JA
, *this, ResponseFileSupport::AtFileCurCP(), WasmOpt
, CmdArgs
,
161 /// Given a base library directory, append path components to form the
163 static std::string
AppendLTOLibDir(const std::string
&Dir
) {
164 // The version allows the path to be keyed to the specific version of
165 // LLVM in used, as the bitcode format is not stable.
166 return Dir
+ "/llvm-lto/" LLVM_VERSION_STRING
;
169 WebAssembly::WebAssembly(const Driver
&D
, const llvm::Triple
&Triple
,
170 const llvm::opt::ArgList
&Args
)
171 : ToolChain(D
, Triple
, Args
) {
173 assert(Triple
.isArch32Bit() != Triple
.isArch64Bit());
175 getProgramPaths().push_back(getDriver().getInstalledDir());
177 auto SysRoot
= getDriver().SysRoot
;
178 if (getTriple().getOS() == llvm::Triple::UnknownOS
) {
179 // Theoretically an "unknown" OS should mean no standard libraries, however
180 // it could also mean that a custom set of libraries is in use, so just add
181 // /lib to the search path. Disable multiarch in this case, to discourage
182 // paths containing "unknown" from acquiring meanings.
183 getFilePaths().push_back(SysRoot
+ "/lib");
185 const std::string MultiarchTriple
=
186 getMultiarchTriple(getDriver(), Triple
, SysRoot
);
187 if (D
.isUsingLTO()) {
188 // For LTO, enable use of lto-enabled sysroot libraries too, if available.
189 // Note that the directory is keyed to the LLVM revision, as LLVM's
190 // bitcode format is not stable.
191 auto Dir
= AppendLTOLibDir(SysRoot
+ "/lib/" + MultiarchTriple
);
192 getFilePaths().push_back(Dir
);
194 getFilePaths().push_back(SysRoot
+ "/lib/" + MultiarchTriple
);
198 bool WebAssembly::IsMathErrnoDefault() const { return false; }
200 bool WebAssembly::IsObjCNonFragileABIDefault() const { return true; }
202 bool WebAssembly::UseObjCMixedDispatch() const { return true; }
204 bool WebAssembly::isPICDefault() const { return false; }
206 bool WebAssembly::isPIEDefault(const llvm::opt::ArgList
&Args
) const {
210 bool WebAssembly::isPICDefaultForced() const { return false; }
212 bool WebAssembly::IsIntegratedAssemblerDefault() const { return true; }
214 bool WebAssembly::hasBlocksRuntime() const { return false; }
216 // TODO: Support profiling.
217 bool WebAssembly::SupportsProfiling() const { return false; }
219 bool WebAssembly::HasNativeLLVMSupport() const { return true; }
221 void WebAssembly::addClangTargetOptions(const ArgList
&DriverArgs
,
222 ArgStringList
&CC1Args
,
223 Action::OffloadKind
) const {
224 if (!DriverArgs
.hasFlag(clang::driver::options::OPT_fuse_init_array
,
225 options::OPT_fno_use_init_array
, true))
226 CC1Args
.push_back("-fno-use-init-array");
228 // '-pthread' implies atomics, bulk-memory, mutable-globals, and sign-ext
229 if (DriverArgs
.hasFlag(options::OPT_pthread
, options::OPT_no_pthread
,
231 if (DriverArgs
.hasFlag(options::OPT_mno_atomics
, options::OPT_matomics
,
233 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
236 if (DriverArgs
.hasFlag(options::OPT_mno_bulk_memory
,
237 options::OPT_mbulk_memory
, false))
238 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
240 << "-mno-bulk-memory";
241 if (DriverArgs
.hasFlag(options::OPT_mno_mutable_globals
,
242 options::OPT_mmutable_globals
, false))
243 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
245 << "-mno-mutable-globals";
246 if (DriverArgs
.hasFlag(options::OPT_mno_sign_ext
, options::OPT_msign_ext
,
248 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
251 CC1Args
.push_back("-target-feature");
252 CC1Args
.push_back("+atomics");
253 CC1Args
.push_back("-target-feature");
254 CC1Args
.push_back("+bulk-memory");
255 CC1Args
.push_back("-target-feature");
256 CC1Args
.push_back("+mutable-globals");
257 CC1Args
.push_back("-target-feature");
258 CC1Args
.push_back("+sign-ext");
261 if (!DriverArgs
.hasFlag(options::OPT_mmutable_globals
,
262 options::OPT_mno_mutable_globals
, false)) {
263 // -fPIC implies +mutable-globals because the PIC ABI used by the linker
264 // depends on importing and exporting mutable globals.
265 llvm::Reloc::Model RelocationModel
;
268 std::tie(RelocationModel
, PICLevel
, IsPIE
) =
269 ParsePICArgs(*this, DriverArgs
);
270 if (RelocationModel
== llvm::Reloc::PIC_
) {
271 if (DriverArgs
.hasFlag(options::OPT_mno_mutable_globals
,
272 options::OPT_mmutable_globals
, false)) {
273 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
275 << "-mno-mutable-globals";
277 CC1Args
.push_back("-target-feature");
278 CC1Args
.push_back("+mutable-globals");
282 if (DriverArgs
.getLastArg(options::OPT_fwasm_exceptions
)) {
283 // '-fwasm-exceptions' is not compatible with '-mno-exception-handling'
284 if (DriverArgs
.hasFlag(options::OPT_mno_exception_handing
,
285 options::OPT_mexception_handing
, false))
286 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
287 << "-fwasm-exceptions"
288 << "-mno-exception-handling";
289 // '-fwasm-exceptions' is not compatible with
290 // '-mllvm -enable-emscripten-cxx-exceptions'
291 for (const Arg
*A
: DriverArgs
.filtered(options::OPT_mllvm
)) {
292 if (StringRef(A
->getValue(0)) == "-enable-emscripten-cxx-exceptions")
293 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
294 << "-fwasm-exceptions"
295 << "-mllvm -enable-emscripten-cxx-exceptions";
297 // '-fwasm-exceptions' implies exception-handling feature
298 CC1Args
.push_back("-target-feature");
299 CC1Args
.push_back("+exception-handling");
300 // Backend needs -wasm-enable-eh to enable Wasm EH
301 CC1Args
.push_back("-mllvm");
302 CC1Args
.push_back("-wasm-enable-eh");
305 for (const Arg
*A
: DriverArgs
.filtered(options::OPT_mllvm
)) {
306 StringRef Opt
= A
->getValue(0);
307 if (Opt
.startswith("-emscripten-cxx-exceptions-allowed")) {
308 // '-mllvm -emscripten-cxx-exceptions-allowed' should be used with
309 // '-mllvm -enable-emscripten-cxx-exceptions'
310 bool EmEHArgExists
= false;
311 for (const Arg
*A
: DriverArgs
.filtered(options::OPT_mllvm
)) {
312 if (StringRef(A
->getValue(0)) == "-enable-emscripten-cxx-exceptions") {
313 EmEHArgExists
= true;
318 getDriver().Diag(diag::err_drv_argument_only_allowed_with
)
319 << "-mllvm -emscripten-cxx-exceptions-allowed"
320 << "-mllvm -enable-emscripten-cxx-exceptions";
322 // Prevent functions specified in -emscripten-cxx-exceptions-allowed list
323 // from being inlined before reaching the wasm backend.
324 StringRef FuncNamesStr
= Opt
.split('=').second
;
325 SmallVector
<StringRef
, 4> FuncNames
;
326 FuncNamesStr
.split(FuncNames
, ',');
327 for (auto Name
: FuncNames
) {
328 CC1Args
.push_back("-mllvm");
329 CC1Args
.push_back(DriverArgs
.MakeArgString("--force-attribute=" + Name
+
334 if (Opt
.startswith("-wasm-enable-sjlj")) {
335 // '-mllvm -wasm-enable-sjlj' is not compatible with
336 // '-mno-exception-handling'
337 if (DriverArgs
.hasFlag(options::OPT_mno_exception_handing
,
338 options::OPT_mexception_handing
, false))
339 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
340 << "-mllvm -wasm-enable-sjlj"
341 << "-mno-exception-handling";
342 // '-mllvm -wasm-enable-sjlj' is not compatible with
343 // '-mllvm -enable-emscripten-cxx-exceptions'
344 // because we don't allow Emscripten EH + Wasm SjLj
345 for (const Arg
*A
: DriverArgs
.filtered(options::OPT_mllvm
)) {
346 if (StringRef(A
->getValue(0)) == "-enable-emscripten-cxx-exceptions")
347 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
348 << "-mllvm -wasm-enable-sjlj"
349 << "-mllvm -enable-emscripten-cxx-exceptions";
351 // '-mllvm -wasm-enable-sjlj' is not compatible with
352 // '-mllvm -enable-emscripten-sjlj'
353 for (const Arg
*A
: DriverArgs
.filtered(options::OPT_mllvm
)) {
354 if (StringRef(A
->getValue(0)) == "-enable-emscripten-sjlj")
355 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
356 << "-mllvm -wasm-enable-sjlj"
357 << "-mllvm -enable-emscripten-sjlj";
359 // '-mllvm -wasm-enable-sjlj' implies exception-handling feature
360 CC1Args
.push_back("-target-feature");
361 CC1Args
.push_back("+exception-handling");
362 // Backend needs '-exception-model=wasm' to use Wasm EH instructions
363 CC1Args
.push_back("-exception-model=wasm");
368 ToolChain::RuntimeLibType
WebAssembly::GetDefaultRuntimeLibType() const {
369 return ToolChain::RLT_CompilerRT
;
372 ToolChain::CXXStdlibType
373 WebAssembly::GetCXXStdlibType(const ArgList
&Args
) const {
374 if (Arg
*A
= Args
.getLastArg(options::OPT_stdlib_EQ
)) {
375 StringRef Value
= A
->getValue();
376 if (Value
== "libc++")
377 return ToolChain::CST_Libcxx
;
378 else if (Value
== "libstdc++")
379 return ToolChain::CST_Libstdcxx
;
381 getDriver().Diag(diag::err_drv_invalid_stdlib_name
)
382 << A
->getAsString(Args
);
384 return ToolChain::CST_Libcxx
;
387 void WebAssembly::AddClangSystemIncludeArgs(const ArgList
&DriverArgs
,
388 ArgStringList
&CC1Args
) const {
389 if (DriverArgs
.hasArg(clang::driver::options::OPT_nostdinc
))
392 const Driver
&D
= getDriver();
394 if (!DriverArgs
.hasArg(options::OPT_nobuiltininc
)) {
395 SmallString
<128> P(D
.ResourceDir
);
396 llvm::sys::path::append(P
, "include");
397 addSystemInclude(DriverArgs
, CC1Args
, P
);
400 if (DriverArgs
.hasArg(options::OPT_nostdlibinc
))
403 // Check for configure-time C include directories.
404 StringRef
CIncludeDirs(C_INCLUDE_DIRS
);
405 if (CIncludeDirs
!= "") {
406 SmallVector
<StringRef
, 5> dirs
;
407 CIncludeDirs
.split(dirs
, ":");
408 for (StringRef dir
: dirs
) {
410 llvm::sys::path::is_absolute(dir
) ? "" : StringRef(D
.SysRoot
);
411 addExternCSystemInclude(DriverArgs
, CC1Args
, Prefix
+ dir
);
416 if (getTriple().getOS() != llvm::Triple::UnknownOS
) {
417 const std::string MultiarchTriple
=
418 getMultiarchTriple(D
, getTriple(), D
.SysRoot
);
419 addSystemInclude(DriverArgs
, CC1Args
, D
.SysRoot
+ "/include/" + MultiarchTriple
);
421 addSystemInclude(DriverArgs
, CC1Args
, D
.SysRoot
+ "/include");
424 void WebAssembly::AddClangCXXStdlibIncludeArgs(const ArgList
&DriverArgs
,
425 ArgStringList
&CC1Args
) const {
427 if (DriverArgs
.hasArg(options::OPT_nostdlibinc
) ||
428 DriverArgs
.hasArg(options::OPT_nostdincxx
))
431 switch (GetCXXStdlibType(DriverArgs
)) {
432 case ToolChain::CST_Libcxx
:
433 addLibCxxIncludePaths(DriverArgs
, CC1Args
);
435 case ToolChain::CST_Libstdcxx
:
436 addLibStdCXXIncludePaths(DriverArgs
, CC1Args
);
441 void WebAssembly::AddCXXStdlibLibArgs(const llvm::opt::ArgList
&Args
,
442 llvm::opt::ArgStringList
&CmdArgs
) const {
444 switch (GetCXXStdlibType(Args
)) {
445 case ToolChain::CST_Libcxx
:
446 CmdArgs
.push_back("-lc++");
447 if (Args
.hasArg(options::OPT_fexperimental_library
))
448 CmdArgs
.push_back("-lc++experimental");
449 CmdArgs
.push_back("-lc++abi");
451 case ToolChain::CST_Libstdcxx
:
452 CmdArgs
.push_back("-lstdc++");
457 SanitizerMask
WebAssembly::getSupportedSanitizers() const {
458 SanitizerMask Res
= ToolChain::getSupportedSanitizers();
459 if (getTriple().isOSEmscripten()) {
460 Res
|= SanitizerKind::Vptr
| SanitizerKind::Leak
| SanitizerKind::Address
;
465 Tool
*WebAssembly::buildLinker() const {
466 return new tools::wasm::Linker(*this);
469 void WebAssembly::addLibCxxIncludePaths(
470 const llvm::opt::ArgList
&DriverArgs
,
471 llvm::opt::ArgStringList
&CC1Args
) const {
472 const Driver
&D
= getDriver();
473 std::string SysRoot
= computeSysRoot();
474 std::string LibPath
= SysRoot
+ "/include";
475 const std::string MultiarchTriple
=
476 getMultiarchTriple(D
, getTriple(), SysRoot
);
477 bool IsKnownOs
= (getTriple().getOS() != llvm::Triple::UnknownOS
);
479 std::string Version
= detectLibcxxVersion(LibPath
);
483 // First add the per-target include path if the OS is known.
485 std::string TargetDir
= LibPath
+ "/" + MultiarchTriple
+ "/c++/" + Version
;
486 addSystemInclude(DriverArgs
, CC1Args
, TargetDir
);
489 // Second add the generic one.
490 addSystemInclude(DriverArgs
, CC1Args
, LibPath
+ "/c++/" + Version
);
493 void WebAssembly::addLibStdCXXIncludePaths(
494 const llvm::opt::ArgList
&DriverArgs
,
495 llvm::opt::ArgStringList
&CC1Args
) const {
496 // We cannot use GCCInstallationDetector here as the sysroot usually does
497 // not contain a full GCC installation.
498 // Instead, we search the given sysroot for /usr/include/xx, similar
499 // to how we do it for libc++.
500 const Driver
&D
= getDriver();
501 std::string SysRoot
= computeSysRoot();
502 std::string LibPath
= SysRoot
+ "/include";
503 const std::string MultiarchTriple
=
504 getMultiarchTriple(D
, getTriple(), SysRoot
);
505 bool IsKnownOs
= (getTriple().getOS() != llvm::Triple::UnknownOS
);
507 // This is similar to detectLibcxxVersion()
511 Generic_GCC::GCCVersion MaxVersion
=
512 Generic_GCC::GCCVersion::Parse("0.0.0");
513 SmallString
<128> Path(LibPath
);
514 llvm::sys::path::append(Path
, "c++");
515 for (llvm::vfs::directory_iterator LI
= getVFS().dir_begin(Path
, EC
), LE
;
516 !EC
&& LI
!= LE
; LI
= LI
.increment(EC
)) {
517 StringRef VersionText
= llvm::sys::path::filename(LI
->path());
518 if (VersionText
[0] != 'v') {
519 auto Version
= Generic_GCC::GCCVersion::Parse(VersionText
);
520 if (Version
> MaxVersion
)
521 MaxVersion
= Version
;
524 if (MaxVersion
.Major
> 0)
525 Version
= MaxVersion
.Text
;
531 // First add the per-target include path if the OS is known.
533 std::string TargetDir
= LibPath
+ "/c++/" + Version
+ "/" + MultiarchTriple
;
534 addSystemInclude(DriverArgs
, CC1Args
, TargetDir
);
537 // Second add the generic one.
538 addSystemInclude(DriverArgs
, CC1Args
, LibPath
+ "/c++/" + Version
);
539 // Third the backward one.
540 addSystemInclude(DriverArgs
, CC1Args
, LibPath
+ "/c++/" + Version
+ "/backward");