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