xref: /freebsd/contrib/llvm-project/clang/lib/Driver/ToolChains/WebAssembly.cpp (revision dab59af3bcc7cb7ba01569d3044894b3e860ad56)
1 //===--- WebAssembly.cpp - WebAssembly ToolChain Implementation -*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "WebAssembly.h"
10 #include "CommonArgs.h"
11 #include "Gnu.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"
22 
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;
28 
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();
36 }
37 
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);
46 
47       // Interpret 'lld' as explicitly requesting `wasm-ld`, so look for that
48       // linker. Note that for `wasm32-wasip2` this overrides the default linker
49       // of `wasm-component-ld`.
50       if (UseLinker == "lld") {
51         return ToolChain.GetProgramPath("wasm-ld");
52       }
53 
54       // Allow 'ld' as an alias for the default linker
55       if (UseLinker != "ld")
56         ToolChain.getDriver().Diag(diag::err_drv_invalid_linker_name)
57             << A->getAsString(Args);
58     }
59   }
60 
61   return ToolChain.GetProgramPath(ToolChain.getDefaultLinker());
62 }
63 
64 void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
65                                 const InputInfo &Output,
66                                 const InputInfoList &Inputs,
67                                 const ArgList &Args,
68                                 const char *LinkingOutput) const {
69 
70   const ToolChain &ToolChain = getToolChain();
71   const char *Linker = Args.MakeArgString(getLinkerPath(Args));
72   ArgStringList CmdArgs;
73 
74   CmdArgs.push_back("-m");
75   if (ToolChain.getTriple().isArch64Bit())
76     CmdArgs.push_back("wasm64");
77   else
78     CmdArgs.push_back("wasm32");
79 
80   if (Args.hasArg(options::OPT_s))
81     CmdArgs.push_back("--strip-all");
82 
83   // On `wasip2` the default linker is `wasm-component-ld` which wraps the
84   // execution of `wasm-ld`. Find `wasm-ld` and pass it as an argument of where
85   // to find it to avoid it needing to hunt and rediscover or search `PATH` for
86   // where it is.
87   if (llvm::sys::path::stem(Linker).ends_with_insensitive(
88           "wasm-component-ld")) {
89     CmdArgs.push_back("--wasm-ld-path");
90     CmdArgs.push_back(Args.MakeArgString(ToolChain.GetProgramPath("wasm-ld")));
91   }
92 
93   Args.addAllArgs(CmdArgs, {options::OPT_L, options::OPT_u});
94 
95   ToolChain.AddFilePathLibArgs(Args, CmdArgs);
96 
97   bool IsCommand = true;
98   const char *Crt1;
99   const char *Entry = nullptr;
100 
101   // When -shared is specified, use the reactor exec model unless
102   // specified otherwise.
103   if (Args.hasArg(options::OPT_shared))
104     IsCommand = false;
105 
106   if (const Arg *A = Args.getLastArg(options::OPT_mexec_model_EQ)) {
107     StringRef CM = A->getValue();
108     if (CM == "command") {
109       IsCommand = true;
110     } else if (CM == "reactor") {
111       IsCommand = false;
112     } else {
113       ToolChain.getDriver().Diag(diag::err_drv_invalid_argument_to_option)
114           << CM << A->getOption().getName();
115     }
116   }
117 
118   if (IsCommand) {
119     // If crt1-command.o exists, it supports new-style commands, so use it.
120     // Otherwise, use the old crt1.o. This is a temporary transition measure.
121     // Once WASI libc no longer needs to support LLVM versions which lack
122     // support for new-style command, it can make crt1.o the same as
123     // crt1-command.o. And once LLVM no longer needs to support WASI libc
124     // versions before that, it can switch to using crt1-command.o.
125     Crt1 = "crt1.o";
126     if (ToolChain.GetFilePath("crt1-command.o") != "crt1-command.o")
127       Crt1 = "crt1-command.o";
128   } else {
129     Crt1 = "crt1-reactor.o";
130     Entry = "_initialize";
131   }
132 
133   if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles))
134     CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(Crt1)));
135   if (Entry) {
136     CmdArgs.push_back(Args.MakeArgString("--entry"));
137     CmdArgs.push_back(Args.MakeArgString(Entry));
138   }
139 
140   if (Args.hasArg(options::OPT_shared))
141     CmdArgs.push_back(Args.MakeArgString("-shared"));
142 
143   AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
144 
145   if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
146     if (ToolChain.ShouldLinkCXXStdlib(Args))
147       ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
148 
149     if (Args.hasArg(options::OPT_pthread)) {
150       CmdArgs.push_back("-lpthread");
151       CmdArgs.push_back("--shared-memory");
152     }
153 
154     CmdArgs.push_back("-lc");
155     AddRunTimeLibs(ToolChain, ToolChain.getDriver(), CmdArgs, Args);
156   }
157 
158   CmdArgs.push_back("-o");
159   CmdArgs.push_back(Output.getFilename());
160 
161   if (Args.hasFlag(options::OPT_wasm_opt, options::OPT_no_wasm_opt, true)) {
162     // When optimizing, if wasm-opt is available, run it.
163     std::string WasmOptPath;
164     if (Args.getLastArg(options::OPT_O_Group)) {
165       WasmOptPath = ToolChain.GetProgramPath("wasm-opt");
166       if (WasmOptPath == "wasm-opt") {
167         WasmOptPath = {};
168       }
169     }
170 
171     if (!WasmOptPath.empty()) {
172       CmdArgs.push_back("--keep-section=target_features");
173     }
174 
175     C.addCommand(std::make_unique<Command>(JA, *this,
176                                            ResponseFileSupport::AtFileCurCP(),
177                                            Linker, CmdArgs, Inputs, Output));
178 
179     if (Arg *A = Args.getLastArg(options::OPT_O_Group)) {
180       if (!WasmOptPath.empty()) {
181         StringRef OOpt = "s";
182         if (A->getOption().matches(options::OPT_O4) ||
183             A->getOption().matches(options::OPT_Ofast))
184           OOpt = "4";
185         else if (A->getOption().matches(options::OPT_O0))
186           OOpt = "0";
187         else if (A->getOption().matches(options::OPT_O))
188           OOpt = A->getValue();
189 
190         if (OOpt != "0") {
191           const char *WasmOpt = Args.MakeArgString(WasmOptPath);
192           ArgStringList OptArgs;
193           OptArgs.push_back(Output.getFilename());
194           OptArgs.push_back(Args.MakeArgString(llvm::Twine("-O") + OOpt));
195           OptArgs.push_back("-o");
196           OptArgs.push_back(Output.getFilename());
197           C.addCommand(std::make_unique<Command>(
198               JA, *this, ResponseFileSupport::AtFileCurCP(), WasmOpt, OptArgs,
199               Inputs, Output));
200         }
201       }
202     }
203   }
204 }
205 
206 /// Given a base library directory, append path components to form the
207 /// LTO directory.
208 static std::string AppendLTOLibDir(const std::string &Dir) {
209     // The version allows the path to be keyed to the specific version of
210     // LLVM in used, as the bitcode format is not stable.
211     return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
212 }
213 
214 WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
215                          const llvm::opt::ArgList &Args)
216     : ToolChain(D, Triple, Args) {
217 
218   assert(Triple.isArch32Bit() != Triple.isArch64Bit());
219 
220   getProgramPaths().push_back(getDriver().Dir);
221 
222   auto SysRoot = getDriver().SysRoot;
223   if (getTriple().getOS() == llvm::Triple::UnknownOS) {
224     // Theoretically an "unknown" OS should mean no standard libraries, however
225     // it could also mean that a custom set of libraries is in use, so just add
226     // /lib to the search path. Disable multiarch in this case, to discourage
227     // paths containing "unknown" from acquiring meanings.
228     getFilePaths().push_back(SysRoot + "/lib");
229   } else {
230     const std::string MultiarchTriple =
231         getMultiarchTriple(getDriver(), Triple, SysRoot);
232     if (D.isUsingLTO()) {
233       // For LTO, enable use of lto-enabled sysroot libraries too, if available.
234       // Note that the directory is keyed to the LLVM revision, as LLVM's
235       // bitcode format is not stable.
236       auto Dir = AppendLTOLibDir(SysRoot + "/lib/" + MultiarchTriple);
237       getFilePaths().push_back(Dir);
238     }
239     getFilePaths().push_back(SysRoot + "/lib/" + MultiarchTriple);
240   }
241 }
242 
243 const char *WebAssembly::getDefaultLinker() const {
244   if (getOS() == "wasip2")
245     return "wasm-component-ld";
246   return "wasm-ld";
247 }
248 
249 bool WebAssembly::IsMathErrnoDefault() const { return false; }
250 
251 bool WebAssembly::IsObjCNonFragileABIDefault() const { return true; }
252 
253 bool WebAssembly::UseObjCMixedDispatch() const { return true; }
254 
255 bool WebAssembly::isPICDefault() const { return false; }
256 
257 bool WebAssembly::isPIEDefault(const llvm::opt::ArgList &Args) const {
258   return false;
259 }
260 
261 bool WebAssembly::isPICDefaultForced() const { return false; }
262 
263 bool WebAssembly::hasBlocksRuntime() const { return false; }
264 
265 // TODO: Support profiling.
266 bool WebAssembly::SupportsProfiling() const { return false; }
267 
268 bool WebAssembly::HasNativeLLVMSupport() const { return true; }
269 
270 void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs,
271                                         ArgStringList &CC1Args,
272                                         Action::OffloadKind) const {
273   if (!DriverArgs.hasFlag(clang::driver::options::OPT_fuse_init_array,
274                           options::OPT_fno_use_init_array, true))
275     CC1Args.push_back("-fno-use-init-array");
276 
277   // '-pthread' implies atomics, bulk-memory, mutable-globals, and sign-ext
278   if (DriverArgs.hasFlag(options::OPT_pthread, options::OPT_no_pthread,
279                          false)) {
280     if (DriverArgs.hasFlag(options::OPT_mno_atomics, options::OPT_matomics,
281                            false))
282       getDriver().Diag(diag::err_drv_argument_not_allowed_with)
283           << "-pthread"
284           << "-mno-atomics";
285     if (DriverArgs.hasFlag(options::OPT_mno_bulk_memory,
286                            options::OPT_mbulk_memory, false))
287       getDriver().Diag(diag::err_drv_argument_not_allowed_with)
288           << "-pthread"
289           << "-mno-bulk-memory";
290     if (DriverArgs.hasFlag(options::OPT_mno_mutable_globals,
291                            options::OPT_mmutable_globals, false))
292       getDriver().Diag(diag::err_drv_argument_not_allowed_with)
293           << "-pthread"
294           << "-mno-mutable-globals";
295     if (DriverArgs.hasFlag(options::OPT_mno_sign_ext, options::OPT_msign_ext,
296                            false))
297       getDriver().Diag(diag::err_drv_argument_not_allowed_with)
298           << "-pthread"
299           << "-mno-sign-ext";
300     CC1Args.push_back("-target-feature");
301     CC1Args.push_back("+atomics");
302     CC1Args.push_back("-target-feature");
303     CC1Args.push_back("+bulk-memory");
304     CC1Args.push_back("-target-feature");
305     CC1Args.push_back("+mutable-globals");
306     CC1Args.push_back("-target-feature");
307     CC1Args.push_back("+sign-ext");
308   }
309 
310   if (!DriverArgs.hasFlag(options::OPT_mmutable_globals,
311                           options::OPT_mno_mutable_globals, false)) {
312     // -fPIC implies +mutable-globals because the PIC ABI used by the linker
313     // depends on importing and exporting mutable globals.
314     llvm::Reloc::Model RelocationModel;
315     unsigned PICLevel;
316     bool IsPIE;
317     std::tie(RelocationModel, PICLevel, IsPIE) =
318         ParsePICArgs(*this, DriverArgs);
319     if (RelocationModel == llvm::Reloc::PIC_) {
320       if (DriverArgs.hasFlag(options::OPT_mno_mutable_globals,
321                              options::OPT_mmutable_globals, false)) {
322         getDriver().Diag(diag::err_drv_argument_not_allowed_with)
323             << "-fPIC"
324             << "-mno-mutable-globals";
325       }
326       CC1Args.push_back("-target-feature");
327       CC1Args.push_back("+mutable-globals");
328     }
329   }
330 
331   if (DriverArgs.getLastArg(options::OPT_fwasm_exceptions)) {
332     // '-fwasm-exceptions' is not compatible with '-mno-exception-handling'
333     if (DriverArgs.hasFlag(options::OPT_mno_exception_handing,
334                            options::OPT_mexception_handing, false))
335       getDriver().Diag(diag::err_drv_argument_not_allowed_with)
336           << "-fwasm-exceptions"
337           << "-mno-exception-handling";
338     // '-fwasm-exceptions' is not compatible with
339     // '-mllvm -enable-emscripten-cxx-exceptions'
340     for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
341       if (StringRef(A->getValue(0)) == "-enable-emscripten-cxx-exceptions")
342         getDriver().Diag(diag::err_drv_argument_not_allowed_with)
343             << "-fwasm-exceptions"
344             << "-mllvm -enable-emscripten-cxx-exceptions";
345     }
346     // '-fwasm-exceptions' implies exception-handling feature
347     CC1Args.push_back("-target-feature");
348     CC1Args.push_back("+exception-handling");
349     // Backend needs -wasm-enable-eh to enable Wasm EH
350     CC1Args.push_back("-mllvm");
351     CC1Args.push_back("-wasm-enable-eh");
352 
353     // New Wasm EH spec (adopted in Oct 2023) requires multivalue and
354     // reference-types.
355     if (DriverArgs.hasFlag(options::OPT_mno_multivalue,
356                            options::OPT_mmultivalue, false)) {
357       getDriver().Diag(diag::err_drv_argument_not_allowed_with)
358           << "-fwasm-exceptions" << "-mno-multivalue";
359     }
360     if (DriverArgs.hasFlag(options::OPT_mno_reference_types,
361                            options::OPT_mreference_types, false)) {
362       getDriver().Diag(diag::err_drv_argument_not_allowed_with)
363           << "-fwasm-exceptions" << "-mno-reference-types";
364     }
365     CC1Args.push_back("-target-feature");
366     CC1Args.push_back("+multivalue");
367     CC1Args.push_back("-target-feature");
368     CC1Args.push_back("+reference-types");
369   }
370 
371   for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
372     StringRef Opt = A->getValue(0);
373     if (Opt.starts_with("-emscripten-cxx-exceptions-allowed")) {
374       // '-mllvm -emscripten-cxx-exceptions-allowed' should be used with
375       // '-mllvm -enable-emscripten-cxx-exceptions'
376       bool EmEHArgExists = false;
377       for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
378         if (StringRef(A->getValue(0)) == "-enable-emscripten-cxx-exceptions") {
379           EmEHArgExists = true;
380           break;
381         }
382       }
383       if (!EmEHArgExists)
384         getDriver().Diag(diag::err_drv_argument_only_allowed_with)
385             << "-mllvm -emscripten-cxx-exceptions-allowed"
386             << "-mllvm -enable-emscripten-cxx-exceptions";
387 
388       // Prevent functions specified in -emscripten-cxx-exceptions-allowed list
389       // from being inlined before reaching the wasm backend.
390       StringRef FuncNamesStr = Opt.split('=').second;
391       SmallVector<StringRef, 4> FuncNames;
392       FuncNamesStr.split(FuncNames, ',');
393       for (auto Name : FuncNames) {
394         CC1Args.push_back("-mllvm");
395         CC1Args.push_back(DriverArgs.MakeArgString("--force-attribute=" + Name +
396                                                    ":noinline"));
397       }
398     }
399 
400     if (Opt.starts_with("-wasm-enable-sjlj")) {
401       // '-mllvm -wasm-enable-sjlj' is not compatible with
402       // '-mno-exception-handling'
403       if (DriverArgs.hasFlag(options::OPT_mno_exception_handing,
404                              options::OPT_mexception_handing, false))
405         getDriver().Diag(diag::err_drv_argument_not_allowed_with)
406             << "-mllvm -wasm-enable-sjlj"
407             << "-mno-exception-handling";
408       // '-mllvm -wasm-enable-sjlj' is not compatible with
409       // '-mllvm -enable-emscripten-cxx-exceptions'
410       // because we don't allow Emscripten EH + Wasm SjLj
411       for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
412         if (StringRef(A->getValue(0)) == "-enable-emscripten-cxx-exceptions")
413           getDriver().Diag(diag::err_drv_argument_not_allowed_with)
414               << "-mllvm -wasm-enable-sjlj"
415               << "-mllvm -enable-emscripten-cxx-exceptions";
416       }
417       // '-mllvm -wasm-enable-sjlj' is not compatible with
418       // '-mllvm -enable-emscripten-sjlj'
419       for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
420         if (StringRef(A->getValue(0)) == "-enable-emscripten-sjlj")
421           getDriver().Diag(diag::err_drv_argument_not_allowed_with)
422               << "-mllvm -wasm-enable-sjlj"
423               << "-mllvm -enable-emscripten-sjlj";
424       }
425       // '-mllvm -wasm-enable-sjlj' implies exception-handling feature
426       CC1Args.push_back("-target-feature");
427       CC1Args.push_back("+exception-handling");
428       // Backend needs '-exception-model=wasm' to use Wasm EH instructions
429       CC1Args.push_back("-exception-model=wasm");
430 
431       // New Wasm EH spec (adopted in Oct 2023) requires multivalue and
432       // reference-types.
433       if (DriverArgs.hasFlag(options::OPT_mno_multivalue,
434                              options::OPT_mmultivalue, false)) {
435         getDriver().Diag(diag::err_drv_argument_not_allowed_with)
436             << "-mllvm -wasm-enable-sjlj" << "-mno-multivalue";
437       }
438       if (DriverArgs.hasFlag(options::OPT_mno_reference_types,
439                              options::OPT_mreference_types, false)) {
440         getDriver().Diag(diag::err_drv_argument_not_allowed_with)
441             << "-mllvm -wasm-enable-sjlj" << "-mno-reference-types";
442       }
443       CC1Args.push_back("-target-feature");
444       CC1Args.push_back("+multivalue");
445       CC1Args.push_back("-target-feature");
446       CC1Args.push_back("+reference-types");
447     }
448   }
449 }
450 
451 ToolChain::RuntimeLibType WebAssembly::GetDefaultRuntimeLibType() const {
452   return ToolChain::RLT_CompilerRT;
453 }
454 
455 ToolChain::CXXStdlibType
456 WebAssembly::GetCXXStdlibType(const ArgList &Args) const {
457   if (Arg *A = Args.getLastArg(options::OPT_stdlib_EQ)) {
458     StringRef Value = A->getValue();
459     if (Value == "libc++")
460       return ToolChain::CST_Libcxx;
461     else if (Value == "libstdc++")
462       return ToolChain::CST_Libstdcxx;
463     else
464       getDriver().Diag(diag::err_drv_invalid_stdlib_name)
465           << A->getAsString(Args);
466   }
467   return ToolChain::CST_Libcxx;
468 }
469 
470 void WebAssembly::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
471                                             ArgStringList &CC1Args) const {
472   if (DriverArgs.hasArg(clang::driver::options::OPT_nostdinc))
473     return;
474 
475   const Driver &D = getDriver();
476 
477   if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
478     SmallString<128> P(D.ResourceDir);
479     llvm::sys::path::append(P, "include");
480     addSystemInclude(DriverArgs, CC1Args, P);
481   }
482 
483   if (DriverArgs.hasArg(options::OPT_nostdlibinc))
484     return;
485 
486   // Check for configure-time C include directories.
487   StringRef CIncludeDirs(C_INCLUDE_DIRS);
488   if (CIncludeDirs != "") {
489     SmallVector<StringRef, 5> dirs;
490     CIncludeDirs.split(dirs, ":");
491     for (StringRef dir : dirs) {
492       StringRef Prefix =
493           llvm::sys::path::is_absolute(dir) ? "" : StringRef(D.SysRoot);
494       addExternCSystemInclude(DriverArgs, CC1Args, Prefix + dir);
495     }
496     return;
497   }
498 
499   if (getTriple().getOS() != llvm::Triple::UnknownOS) {
500     const std::string MultiarchTriple =
501         getMultiarchTriple(D, getTriple(), D.SysRoot);
502     addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include/" + MultiarchTriple);
503   }
504   addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include");
505 }
506 
507 void WebAssembly::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
508                                                ArgStringList &CC1Args) const {
509 
510   if (DriverArgs.hasArg(options::OPT_nostdlibinc, options::OPT_nostdinc,
511                         options::OPT_nostdincxx))
512     return;
513 
514   switch (GetCXXStdlibType(DriverArgs)) {
515   case ToolChain::CST_Libcxx:
516     addLibCxxIncludePaths(DriverArgs, CC1Args);
517     break;
518   case ToolChain::CST_Libstdcxx:
519     addLibStdCXXIncludePaths(DriverArgs, CC1Args);
520     break;
521   }
522 }
523 
524 void WebAssembly::AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args,
525                                       llvm::opt::ArgStringList &CmdArgs) const {
526 
527   switch (GetCXXStdlibType(Args)) {
528   case ToolChain::CST_Libcxx:
529     CmdArgs.push_back("-lc++");
530     if (Args.hasArg(options::OPT_fexperimental_library))
531       CmdArgs.push_back("-lc++experimental");
532     CmdArgs.push_back("-lc++abi");
533     break;
534   case ToolChain::CST_Libstdcxx:
535     CmdArgs.push_back("-lstdc++");
536     break;
537   }
538 }
539 
540 SanitizerMask WebAssembly::getSupportedSanitizers() const {
541   SanitizerMask Res = ToolChain::getSupportedSanitizers();
542   if (getTriple().isOSEmscripten()) {
543     Res |= SanitizerKind::Vptr | SanitizerKind::Leak | SanitizerKind::Address;
544   }
545   // -fsanitize=function places two words before the function label, which are
546   // -unsupported.
547   Res &= ~SanitizerKind::Function;
548   return Res;
549 }
550 
551 Tool *WebAssembly::buildLinker() const {
552   return new tools::wasm::Linker(*this);
553 }
554 
555 void WebAssembly::addLibCxxIncludePaths(
556     const llvm::opt::ArgList &DriverArgs,
557     llvm::opt::ArgStringList &CC1Args) const {
558   const Driver &D = getDriver();
559   std::string SysRoot = computeSysRoot();
560   std::string LibPath = SysRoot + "/include";
561   const std::string MultiarchTriple =
562       getMultiarchTriple(D, getTriple(), SysRoot);
563   bool IsKnownOs = (getTriple().getOS() != llvm::Triple::UnknownOS);
564 
565   std::string Version = detectLibcxxVersion(LibPath);
566   if (Version.empty())
567     return;
568 
569   // First add the per-target include path if the OS is known.
570   if (IsKnownOs) {
571     std::string TargetDir = LibPath + "/" + MultiarchTriple + "/c++/" + Version;
572     addSystemInclude(DriverArgs, CC1Args, TargetDir);
573   }
574 
575   // Second add the generic one.
576   addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
577 }
578 
579 void WebAssembly::addLibStdCXXIncludePaths(
580     const llvm::opt::ArgList &DriverArgs,
581     llvm::opt::ArgStringList &CC1Args) const {
582   // We cannot use GCCInstallationDetector here as the sysroot usually does
583   // not contain a full GCC installation.
584   // Instead, we search the given sysroot for /usr/include/xx, similar
585   // to how we do it for libc++.
586   const Driver &D = getDriver();
587   std::string SysRoot = computeSysRoot();
588   std::string LibPath = SysRoot + "/include";
589   const std::string MultiarchTriple =
590       getMultiarchTriple(D, getTriple(), SysRoot);
591   bool IsKnownOs = (getTriple().getOS() != llvm::Triple::UnknownOS);
592 
593   // This is similar to detectLibcxxVersion()
594   std::string Version;
595   {
596     std::error_code EC;
597     Generic_GCC::GCCVersion MaxVersion =
598         Generic_GCC::GCCVersion::Parse("0.0.0");
599     SmallString<128> Path(LibPath);
600     llvm::sys::path::append(Path, "c++");
601     for (llvm::vfs::directory_iterator LI = getVFS().dir_begin(Path, EC), LE;
602          !EC && LI != LE; LI = LI.increment(EC)) {
603       StringRef VersionText = llvm::sys::path::filename(LI->path());
604       if (VersionText[0] != 'v') {
605         auto Version = Generic_GCC::GCCVersion::Parse(VersionText);
606         if (Version > MaxVersion)
607           MaxVersion = Version;
608       }
609     }
610     if (MaxVersion.Major > 0)
611       Version = MaxVersion.Text;
612   }
613 
614   if (Version.empty())
615     return;
616 
617   // First add the per-target include path if the OS is known.
618   if (IsKnownOs) {
619     std::string TargetDir = LibPath + "/c++/" + Version + "/" + MultiarchTriple;
620     addSystemInclude(DriverArgs, CC1Args, TargetDir);
621   }
622 
623   // Second add the generic one.
624   addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version);
625   // Third the backward one.
626   addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version + "/backward");
627 }
628