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
, options::OPT_u
});
78 ToolChain
.AddFilePathLibArgs(Args
, CmdArgs
);
80 bool IsCommand
= true;
82 const char *Entry
= nullptr;
84 // When -shared is specified, use the reactor exec model unless
85 // specified otherwise.
86 if (Args
.hasArg(options::OPT_shared
))
89 if (const Arg
*A
= Args
.getLastArg(options::OPT_mexec_model_EQ
)) {
90 StringRef CM
= A
->getValue();
91 if (CM
== "command") {
93 } else if (CM
== "reactor") {
96 ToolChain
.getDriver().Diag(diag::err_drv_invalid_argument_to_option
)
97 << CM
<< A
->getOption().getName();
102 // If crt1-command.o exists, it supports new-style commands, so use it.
103 // Otherwise, use the old crt1.o. This is a temporary transition measure.
104 // Once WASI libc no longer needs to support LLVM versions which lack
105 // support for new-style command, it can make crt1.o the same as
106 // crt1-command.o. And once LLVM no longer needs to support WASI libc
107 // versions before that, it can switch to using crt1-command.o.
109 if (ToolChain
.GetFilePath("crt1-command.o") != "crt1-command.o")
110 Crt1
= "crt1-command.o";
112 Crt1
= "crt1-reactor.o";
113 Entry
= "_initialize";
116 if (!Args
.hasArg(options::OPT_nostdlib
, options::OPT_nostartfiles
))
117 CmdArgs
.push_back(Args
.MakeArgString(ToolChain
.GetFilePath(Crt1
)));
119 CmdArgs
.push_back(Args
.MakeArgString("--entry"));
120 CmdArgs
.push_back(Args
.MakeArgString(Entry
));
123 if (Args
.hasArg(options::OPT_shared
))
124 CmdArgs
.push_back(Args
.MakeArgString("-shared"));
126 AddLinkerInputs(ToolChain
, Inputs
, Args
, CmdArgs
, JA
);
128 if (!Args
.hasArg(options::OPT_nostdlib
, options::OPT_nodefaultlibs
)) {
129 if (ToolChain
.ShouldLinkCXXStdlib(Args
))
130 ToolChain
.AddCXXStdlibLibArgs(Args
, CmdArgs
);
132 if (Args
.hasArg(options::OPT_pthread
)) {
133 CmdArgs
.push_back("-lpthread");
134 CmdArgs
.push_back("--shared-memory");
137 CmdArgs
.push_back("-lc");
138 AddRunTimeLibs(ToolChain
, ToolChain
.getDriver(), CmdArgs
, Args
);
141 CmdArgs
.push_back("-o");
142 CmdArgs
.push_back(Output
.getFilename());
144 // When optimizing, if wasm-opt is available, run it.
145 std::string WasmOptPath
;
146 if (Args
.getLastArg(options::OPT_O_Group
)) {
147 WasmOptPath
= ToolChain
.GetProgramPath("wasm-opt");
148 if (WasmOptPath
== "wasm-opt") {
153 if (!WasmOptPath
.empty()) {
154 CmdArgs
.push_back("--keep-section=target_features");
157 C
.addCommand(std::make_unique
<Command
>(JA
, *this,
158 ResponseFileSupport::AtFileCurCP(),
159 Linker
, CmdArgs
, Inputs
, Output
));
161 if (Arg
*A
= Args
.getLastArg(options::OPT_O_Group
)) {
162 if (!WasmOptPath
.empty()) {
163 StringRef OOpt
= "s";
164 if (A
->getOption().matches(options::OPT_O4
) ||
165 A
->getOption().matches(options::OPT_Ofast
))
167 else if (A
->getOption().matches(options::OPT_O0
))
169 else if (A
->getOption().matches(options::OPT_O
))
170 OOpt
= A
->getValue();
173 const char *WasmOpt
= Args
.MakeArgString(WasmOptPath
);
174 ArgStringList OptArgs
;
175 OptArgs
.push_back(Output
.getFilename());
176 OptArgs
.push_back(Args
.MakeArgString(llvm::Twine("-O") + OOpt
));
177 OptArgs
.push_back("-o");
178 OptArgs
.push_back(Output
.getFilename());
179 C
.addCommand(std::make_unique
<Command
>(
180 JA
, *this, ResponseFileSupport::AtFileCurCP(), WasmOpt
, OptArgs
,
187 /// Given a base library directory, append path components to form the
189 static std::string
AppendLTOLibDir(const std::string
&Dir
) {
190 // The version allows the path to be keyed to the specific version of
191 // LLVM in used, as the bitcode format is not stable.
192 return Dir
+ "/llvm-lto/" LLVM_VERSION_STRING
;
195 WebAssembly::WebAssembly(const Driver
&D
, const llvm::Triple
&Triple
,
196 const llvm::opt::ArgList
&Args
)
197 : ToolChain(D
, Triple
, Args
) {
199 assert(Triple
.isArch32Bit() != Triple
.isArch64Bit());
201 getProgramPaths().push_back(getDriver().getInstalledDir());
203 auto SysRoot
= getDriver().SysRoot
;
204 if (getTriple().getOS() == llvm::Triple::UnknownOS
) {
205 // Theoretically an "unknown" OS should mean no standard libraries, however
206 // it could also mean that a custom set of libraries is in use, so just add
207 // /lib to the search path. Disable multiarch in this case, to discourage
208 // paths containing "unknown" from acquiring meanings.
209 getFilePaths().push_back(SysRoot
+ "/lib");
211 const std::string MultiarchTriple
=
212 getMultiarchTriple(getDriver(), Triple
, SysRoot
);
213 if (D
.isUsingLTO()) {
214 // For LTO, enable use of lto-enabled sysroot libraries too, if available.
215 // Note that the directory is keyed to the LLVM revision, as LLVM's
216 // bitcode format is not stable.
217 auto Dir
= AppendLTOLibDir(SysRoot
+ "/lib/" + MultiarchTriple
);
218 getFilePaths().push_back(Dir
);
220 getFilePaths().push_back(SysRoot
+ "/lib/" + MultiarchTriple
);
224 bool WebAssembly::IsMathErrnoDefault() const { return false; }
226 bool WebAssembly::IsObjCNonFragileABIDefault() const { return true; }
228 bool WebAssembly::UseObjCMixedDispatch() const { return true; }
230 bool WebAssembly::isPICDefault() const { return false; }
232 bool WebAssembly::isPIEDefault(const llvm::opt::ArgList
&Args
) const {
236 bool WebAssembly::isPICDefaultForced() const { return false; }
238 bool WebAssembly::hasBlocksRuntime() const { return false; }
240 // TODO: Support profiling.
241 bool WebAssembly::SupportsProfiling() const { return false; }
243 bool WebAssembly::HasNativeLLVMSupport() const { return true; }
245 void WebAssembly::addClangTargetOptions(const ArgList
&DriverArgs
,
246 ArgStringList
&CC1Args
,
247 Action::OffloadKind
) const {
248 if (!DriverArgs
.hasFlag(clang::driver::options::OPT_fuse_init_array
,
249 options::OPT_fno_use_init_array
, true))
250 CC1Args
.push_back("-fno-use-init-array");
252 // '-pthread' implies atomics, bulk-memory, mutable-globals, and sign-ext
253 if (DriverArgs
.hasFlag(options::OPT_pthread
, options::OPT_no_pthread
,
255 if (DriverArgs
.hasFlag(options::OPT_mno_atomics
, options::OPT_matomics
,
257 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
260 if (DriverArgs
.hasFlag(options::OPT_mno_bulk_memory
,
261 options::OPT_mbulk_memory
, false))
262 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
264 << "-mno-bulk-memory";
265 if (DriverArgs
.hasFlag(options::OPT_mno_mutable_globals
,
266 options::OPT_mmutable_globals
, false))
267 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
269 << "-mno-mutable-globals";
270 if (DriverArgs
.hasFlag(options::OPT_mno_sign_ext
, options::OPT_msign_ext
,
272 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
275 CC1Args
.push_back("-target-feature");
276 CC1Args
.push_back("+atomics");
277 CC1Args
.push_back("-target-feature");
278 CC1Args
.push_back("+bulk-memory");
279 CC1Args
.push_back("-target-feature");
280 CC1Args
.push_back("+mutable-globals");
281 CC1Args
.push_back("-target-feature");
282 CC1Args
.push_back("+sign-ext");
285 if (!DriverArgs
.hasFlag(options::OPT_mmutable_globals
,
286 options::OPT_mno_mutable_globals
, false)) {
287 // -fPIC implies +mutable-globals because the PIC ABI used by the linker
288 // depends on importing and exporting mutable globals.
289 llvm::Reloc::Model RelocationModel
;
292 std::tie(RelocationModel
, PICLevel
, IsPIE
) =
293 ParsePICArgs(*this, DriverArgs
);
294 if (RelocationModel
== llvm::Reloc::PIC_
) {
295 if (DriverArgs
.hasFlag(options::OPT_mno_mutable_globals
,
296 options::OPT_mmutable_globals
, false)) {
297 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
299 << "-mno-mutable-globals";
301 CC1Args
.push_back("-target-feature");
302 CC1Args
.push_back("+mutable-globals");
306 if (DriverArgs
.getLastArg(options::OPT_fwasm_exceptions
)) {
307 // '-fwasm-exceptions' is not compatible with '-mno-exception-handling'
308 if (DriverArgs
.hasFlag(options::OPT_mno_exception_handing
,
309 options::OPT_mexception_handing
, false))
310 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
311 << "-fwasm-exceptions"
312 << "-mno-exception-handling";
313 // '-fwasm-exceptions' is not compatible with
314 // '-mllvm -enable-emscripten-cxx-exceptions'
315 for (const Arg
*A
: DriverArgs
.filtered(options::OPT_mllvm
)) {
316 if (StringRef(A
->getValue(0)) == "-enable-emscripten-cxx-exceptions")
317 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
318 << "-fwasm-exceptions"
319 << "-mllvm -enable-emscripten-cxx-exceptions";
321 // '-fwasm-exceptions' implies exception-handling feature
322 CC1Args
.push_back("-target-feature");
323 CC1Args
.push_back("+exception-handling");
324 // Backend needs -wasm-enable-eh to enable Wasm EH
325 CC1Args
.push_back("-mllvm");
326 CC1Args
.push_back("-wasm-enable-eh");
329 for (const Arg
*A
: DriverArgs
.filtered(options::OPT_mllvm
)) {
330 StringRef Opt
= A
->getValue(0);
331 if (Opt
.starts_with("-emscripten-cxx-exceptions-allowed")) {
332 // '-mllvm -emscripten-cxx-exceptions-allowed' should be used with
333 // '-mllvm -enable-emscripten-cxx-exceptions'
334 bool EmEHArgExists
= false;
335 for (const Arg
*A
: DriverArgs
.filtered(options::OPT_mllvm
)) {
336 if (StringRef(A
->getValue(0)) == "-enable-emscripten-cxx-exceptions") {
337 EmEHArgExists
= true;
342 getDriver().Diag(diag::err_drv_argument_only_allowed_with
)
343 << "-mllvm -emscripten-cxx-exceptions-allowed"
344 << "-mllvm -enable-emscripten-cxx-exceptions";
346 // Prevent functions specified in -emscripten-cxx-exceptions-allowed list
347 // from being inlined before reaching the wasm backend.
348 StringRef FuncNamesStr
= Opt
.split('=').second
;
349 SmallVector
<StringRef
, 4> FuncNames
;
350 FuncNamesStr
.split(FuncNames
, ',');
351 for (auto Name
: FuncNames
) {
352 CC1Args
.push_back("-mllvm");
353 CC1Args
.push_back(DriverArgs
.MakeArgString("--force-attribute=" + Name
+
358 if (Opt
.starts_with("-wasm-enable-sjlj")) {
359 // '-mllvm -wasm-enable-sjlj' is not compatible with
360 // '-mno-exception-handling'
361 if (DriverArgs
.hasFlag(options::OPT_mno_exception_handing
,
362 options::OPT_mexception_handing
, false))
363 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
364 << "-mllvm -wasm-enable-sjlj"
365 << "-mno-exception-handling";
366 // '-mllvm -wasm-enable-sjlj' is not compatible with
367 // '-mllvm -enable-emscripten-cxx-exceptions'
368 // because we don't allow Emscripten EH + Wasm SjLj
369 for (const Arg
*A
: DriverArgs
.filtered(options::OPT_mllvm
)) {
370 if (StringRef(A
->getValue(0)) == "-enable-emscripten-cxx-exceptions")
371 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
372 << "-mllvm -wasm-enable-sjlj"
373 << "-mllvm -enable-emscripten-cxx-exceptions";
375 // '-mllvm -wasm-enable-sjlj' is not compatible with
376 // '-mllvm -enable-emscripten-sjlj'
377 for (const Arg
*A
: DriverArgs
.filtered(options::OPT_mllvm
)) {
378 if (StringRef(A
->getValue(0)) == "-enable-emscripten-sjlj")
379 getDriver().Diag(diag::err_drv_argument_not_allowed_with
)
380 << "-mllvm -wasm-enable-sjlj"
381 << "-mllvm -enable-emscripten-sjlj";
383 // '-mllvm -wasm-enable-sjlj' implies exception-handling feature
384 CC1Args
.push_back("-target-feature");
385 CC1Args
.push_back("+exception-handling");
386 // Backend needs '-exception-model=wasm' to use Wasm EH instructions
387 CC1Args
.push_back("-exception-model=wasm");
392 ToolChain::RuntimeLibType
WebAssembly::GetDefaultRuntimeLibType() const {
393 return ToolChain::RLT_CompilerRT
;
396 ToolChain::CXXStdlibType
397 WebAssembly::GetCXXStdlibType(const ArgList
&Args
) const {
398 if (Arg
*A
= Args
.getLastArg(options::OPT_stdlib_EQ
)) {
399 StringRef Value
= A
->getValue();
400 if (Value
== "libc++")
401 return ToolChain::CST_Libcxx
;
402 else if (Value
== "libstdc++")
403 return ToolChain::CST_Libstdcxx
;
405 getDriver().Diag(diag::err_drv_invalid_stdlib_name
)
406 << A
->getAsString(Args
);
408 return ToolChain::CST_Libcxx
;
411 void WebAssembly::AddClangSystemIncludeArgs(const ArgList
&DriverArgs
,
412 ArgStringList
&CC1Args
) const {
413 if (DriverArgs
.hasArg(clang::driver::options::OPT_nostdinc
))
416 const Driver
&D
= getDriver();
418 if (!DriverArgs
.hasArg(options::OPT_nobuiltininc
)) {
419 SmallString
<128> P(D
.ResourceDir
);
420 llvm::sys::path::append(P
, "include");
421 addSystemInclude(DriverArgs
, CC1Args
, P
);
424 if (DriverArgs
.hasArg(options::OPT_nostdlibinc
))
427 // Check for configure-time C include directories.
428 StringRef
CIncludeDirs(C_INCLUDE_DIRS
);
429 if (CIncludeDirs
!= "") {
430 SmallVector
<StringRef
, 5> dirs
;
431 CIncludeDirs
.split(dirs
, ":");
432 for (StringRef dir
: dirs
) {
434 llvm::sys::path::is_absolute(dir
) ? "" : StringRef(D
.SysRoot
);
435 addExternCSystemInclude(DriverArgs
, CC1Args
, Prefix
+ dir
);
440 if (getTriple().getOS() != llvm::Triple::UnknownOS
) {
441 const std::string MultiarchTriple
=
442 getMultiarchTriple(D
, getTriple(), D
.SysRoot
);
443 addSystemInclude(DriverArgs
, CC1Args
, D
.SysRoot
+ "/include/" + MultiarchTriple
);
445 addSystemInclude(DriverArgs
, CC1Args
, D
.SysRoot
+ "/include");
448 void WebAssembly::AddClangCXXStdlibIncludeArgs(const ArgList
&DriverArgs
,
449 ArgStringList
&CC1Args
) const {
451 if (DriverArgs
.hasArg(options::OPT_nostdlibinc
, options::OPT_nostdinc
,
452 options::OPT_nostdincxx
))
455 switch (GetCXXStdlibType(DriverArgs
)) {
456 case ToolChain::CST_Libcxx
:
457 addLibCxxIncludePaths(DriverArgs
, CC1Args
);
459 case ToolChain::CST_Libstdcxx
:
460 addLibStdCXXIncludePaths(DriverArgs
, CC1Args
);
465 void WebAssembly::AddCXXStdlibLibArgs(const llvm::opt::ArgList
&Args
,
466 llvm::opt::ArgStringList
&CmdArgs
) const {
468 switch (GetCXXStdlibType(Args
)) {
469 case ToolChain::CST_Libcxx
:
470 CmdArgs
.push_back("-lc++");
471 if (Args
.hasArg(options::OPT_fexperimental_library
))
472 CmdArgs
.push_back("-lc++experimental");
473 CmdArgs
.push_back("-lc++abi");
475 case ToolChain::CST_Libstdcxx
:
476 CmdArgs
.push_back("-lstdc++");
481 SanitizerMask
WebAssembly::getSupportedSanitizers() const {
482 SanitizerMask Res
= ToolChain::getSupportedSanitizers();
483 if (getTriple().isOSEmscripten()) {
484 Res
|= SanitizerKind::Vptr
| SanitizerKind::Leak
| SanitizerKind::Address
;
486 // -fsanitize=function places two words before the function label, which are
488 Res
&= ~SanitizerKind::Function
;
492 Tool
*WebAssembly::buildLinker() const {
493 return new tools::wasm::Linker(*this);
496 void WebAssembly::addLibCxxIncludePaths(
497 const llvm::opt::ArgList
&DriverArgs
,
498 llvm::opt::ArgStringList
&CC1Args
) const {
499 const Driver
&D
= getDriver();
500 std::string SysRoot
= computeSysRoot();
501 std::string LibPath
= SysRoot
+ "/include";
502 const std::string MultiarchTriple
=
503 getMultiarchTriple(D
, getTriple(), SysRoot
);
504 bool IsKnownOs
= (getTriple().getOS() != llvm::Triple::UnknownOS
);
506 std::string Version
= detectLibcxxVersion(LibPath
);
510 // First add the per-target include path if the OS is known.
512 std::string TargetDir
= LibPath
+ "/" + MultiarchTriple
+ "/c++/" + Version
;
513 addSystemInclude(DriverArgs
, CC1Args
, TargetDir
);
516 // Second add the generic one.
517 addSystemInclude(DriverArgs
, CC1Args
, LibPath
+ "/c++/" + Version
);
520 void WebAssembly::addLibStdCXXIncludePaths(
521 const llvm::opt::ArgList
&DriverArgs
,
522 llvm::opt::ArgStringList
&CC1Args
) const {
523 // We cannot use GCCInstallationDetector here as the sysroot usually does
524 // not contain a full GCC installation.
525 // Instead, we search the given sysroot for /usr/include/xx, similar
526 // to how we do it for libc++.
527 const Driver
&D
= getDriver();
528 std::string SysRoot
= computeSysRoot();
529 std::string LibPath
= SysRoot
+ "/include";
530 const std::string MultiarchTriple
=
531 getMultiarchTriple(D
, getTriple(), SysRoot
);
532 bool IsKnownOs
= (getTriple().getOS() != llvm::Triple::UnknownOS
);
534 // This is similar to detectLibcxxVersion()
538 Generic_GCC::GCCVersion MaxVersion
=
539 Generic_GCC::GCCVersion::Parse("0.0.0");
540 SmallString
<128> Path(LibPath
);
541 llvm::sys::path::append(Path
, "c++");
542 for (llvm::vfs::directory_iterator LI
= getVFS().dir_begin(Path
, EC
), LE
;
543 !EC
&& LI
!= LE
; LI
= LI
.increment(EC
)) {
544 StringRef VersionText
= llvm::sys::path::filename(LI
->path());
545 if (VersionText
[0] != 'v') {
546 auto Version
= Generic_GCC::GCCVersion::Parse(VersionText
);
547 if (Version
> MaxVersion
)
548 MaxVersion
= Version
;
551 if (MaxVersion
.Major
> 0)
552 Version
= MaxVersion
.Text
;
558 // First add the per-target include path if the OS is known.
560 std::string TargetDir
= LibPath
+ "/c++/" + Version
+ "/" + MultiarchTriple
;
561 addSystemInclude(DriverArgs
, CC1Args
, TargetDir
);
564 // Second add the generic one.
565 addSystemInclude(DriverArgs
, CC1Args
, LibPath
+ "/c++/" + Version
);
566 // Third the backward one.
567 addSystemInclude(DriverArgs
, CC1Args
, LibPath
+ "/c++/" + Version
+ "/backward");