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 "clang/Driver/Compilation.h"
12 #include "clang/Driver/Driver.h"
13 #include "clang/Driver/DriverDiagnostic.h"
14 #include "clang/Driver/Options.h"
15 #include "llvm/Option/ArgList.h"
16 
17 using namespace clang::driver;
18 using namespace clang::driver::tools;
19 using namespace clang::driver::toolchains;
20 using namespace clang;
21 using namespace llvm::opt;
22 
23 void parseThreadArgs(const Driver &Driver, const ArgList &DriverArgs,
24                      bool &Pthread, StringRef &ThreadModel,
25                      bool CheckForErrors = true) {
26   // Default value for -pthread / -mthread-model options, each being false /
27   // "single".
28   Pthread =
29       DriverArgs.hasFlag(options::OPT_pthread, options::OPT_no_pthread, false);
30   ThreadModel =
31       DriverArgs.getLastArgValue(options::OPT_mthread_model, "single");
32   if (!CheckForErrors)
33     return;
34 
35   // Did user explicitly specify -mthread-model / -pthread?
36   bool HasThreadModel = DriverArgs.hasArg(options::OPT_mthread_model);
37   bool HasPthread = Pthread && DriverArgs.hasArg(options::OPT_pthread);
38   // '-pthread' cannot be used with '-mthread-model single'
39   if (HasPthread && HasThreadModel && ThreadModel == "single")
40     Driver.Diag(diag::err_drv_argument_not_allowed_with)
41         << "-pthread" << "-mthread-model single";
42 }
43 
44 wasm::Linker::Linker(const ToolChain &TC)
45     : GnuTool("wasm::Linker", "lld", TC) {}
46 
47 /// Following the conventions in https://wiki.debian.org/Multiarch/Tuples,
48 /// we remove the vendor field to form the multiarch triple.
49 static std::string getMultiarchTriple(const Driver &D,
50                                       const llvm::Triple &TargetTriple,
51                                       StringRef SysRoot) {
52     return (TargetTriple.getArchName() + "-" +
53             TargetTriple.getOSAndEnvironmentName()).str();
54 }
55 
56 bool wasm::Linker::isLinkJob() const { return true; }
57 
58 bool wasm::Linker::hasIntegratedCPP() const { return false; }
59 
60 void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
61                                 const InputInfo &Output,
62                                 const InputInfoList &Inputs,
63                                 const ArgList &Args,
64                                 const char *LinkingOutput) const {
65 
66   const ToolChain &ToolChain = getToolChain();
67   const char *Linker = Args.MakeArgString(ToolChain.GetLinkerPath());
68   ArgStringList CmdArgs;
69 
70   if (Args.hasArg(options::OPT_s))
71     CmdArgs.push_back("--strip-all");
72 
73   Args.AddAllArgs(CmdArgs, options::OPT_L);
74   Args.AddAllArgs(CmdArgs, options::OPT_u);
75   ToolChain.AddFilePathLibArgs(Args, CmdArgs);
76 
77   if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles))
78     CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crt1.o")));
79 
80   AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
81 
82   if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
83     if (ToolChain.ShouldLinkCXXStdlib(Args))
84       ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
85 
86     if (Args.hasArg(options::OPT_pthread))
87       CmdArgs.push_back("-lpthread");
88 
89     CmdArgs.push_back("-lc");
90     AddRunTimeLibs(ToolChain, ToolChain.getDriver(), CmdArgs, Args);
91   }
92 
93   CmdArgs.push_back("-o");
94   CmdArgs.push_back(Output.getFilename());
95 
96   C.addCommand(llvm::make_unique<Command>(JA, *this, Linker, CmdArgs, Inputs));
97 }
98 
99 WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
100                          const llvm::opt::ArgList &Args)
101     : ToolChain(D, Triple, Args) {
102 
103   assert(Triple.isArch32Bit() != Triple.isArch64Bit());
104 
105   getProgramPaths().push_back(getDriver().getInstalledDir());
106 
107   if (getTriple().getOS() == llvm::Triple::UnknownOS) {
108     // Theoretically an "unknown" OS should mean no standard libraries, however
109     // it could also mean that a custom set of libraries is in use, so just add
110     // /lib to the search path. Disable multiarch in this case, to discourage
111     // paths containing "unknown" from acquiring meanings.
112     getFilePaths().push_back(getDriver().SysRoot + "/lib");
113   } else {
114     const std::string MultiarchTriple =
115         getMultiarchTriple(getDriver(), Triple, getDriver().SysRoot);
116     getFilePaths().push_back(getDriver().SysRoot + "/lib/" + MultiarchTriple);
117   }
118 }
119 
120 bool WebAssembly::IsMathErrnoDefault() const { return false; }
121 
122 bool WebAssembly::IsObjCNonFragileABIDefault() const { return true; }
123 
124 bool WebAssembly::UseObjCMixedDispatch() const { return true; }
125 
126 bool WebAssembly::isPICDefault() const { return false; }
127 
128 bool WebAssembly::isPIEDefault() const { return false; }
129 
130 bool WebAssembly::isPICDefaultForced() const { return false; }
131 
132 bool WebAssembly::IsIntegratedAssemblerDefault() const { return true; }
133 
134 bool WebAssembly::hasBlocksRuntime() const { return false; }
135 
136 // TODO: Support profiling.
137 bool WebAssembly::SupportsProfiling() const { return false; }
138 
139 bool WebAssembly::HasNativeLLVMSupport() const { return true; }
140 
141 void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs,
142                                         ArgStringList &CC1Args,
143                                         Action::OffloadKind) const {
144   if (DriverArgs.hasFlag(clang::driver::options::OPT_fuse_init_array,
145                          options::OPT_fno_use_init_array, true))
146     CC1Args.push_back("-fuse-init-array");
147 
148   // Either '-mthread-model posix' or '-pthread' sets '-target-feature
149   // +atomics'. We intentionally didn't create '-matomics' and set the atomics
150   // target feature here depending on the other two options.
151   bool Pthread = false;
152   StringRef ThreadModel = "";
153   parseThreadArgs(getDriver(), DriverArgs, Pthread, ThreadModel);
154   if (Pthread || ThreadModel != "single") {
155     CC1Args.push_back("-target-feature");
156     CC1Args.push_back("+atomics");
157   }
158 }
159 
160 ToolChain::RuntimeLibType WebAssembly::GetDefaultRuntimeLibType() const {
161   return ToolChain::RLT_CompilerRT;
162 }
163 
164 ToolChain::CXXStdlibType
165 WebAssembly::GetCXXStdlibType(const ArgList &Args) const {
166   if (Arg *A = Args.getLastArg(options::OPT_stdlib_EQ)) {
167     StringRef Value = A->getValue();
168     if (Value != "libc++")
169       getDriver().Diag(diag::err_drv_invalid_stdlib_name)
170           << A->getAsString(Args);
171   }
172   return ToolChain::CST_Libcxx;
173 }
174 
175 void WebAssembly::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
176                                             ArgStringList &CC1Args) const {
177   if (!DriverArgs.hasArg(options::OPT_nostdinc)) {
178     if (getTriple().getOS() != llvm::Triple::UnknownOS) {
179       const std::string MultiarchTriple =
180           getMultiarchTriple(getDriver(), getTriple(), getDriver().SysRoot);
181       addSystemInclude(DriverArgs, CC1Args, getDriver().SysRoot + "/include/" + MultiarchTriple);
182     }
183     addSystemInclude(DriverArgs, CC1Args, getDriver().SysRoot + "/include");
184   }
185 }
186 
187 void WebAssembly::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
188                                                ArgStringList &CC1Args) const {
189   if (!DriverArgs.hasArg(options::OPT_nostdlibinc) &&
190       !DriverArgs.hasArg(options::OPT_nostdincxx)) {
191     if (getTriple().getOS() != llvm::Triple::UnknownOS) {
192       const std::string MultiarchTriple =
193           getMultiarchTriple(getDriver(), getTriple(), getDriver().SysRoot);
194       addSystemInclude(DriverArgs, CC1Args,
195                        getDriver().SysRoot + "/include/" + MultiarchTriple + "/c++/v1");
196     }
197     addSystemInclude(DriverArgs, CC1Args,
198                      getDriver().SysRoot + "/include/c++/v1");
199   }
200 }
201 
202 void WebAssembly::AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args,
203                                       llvm::opt::ArgStringList &CmdArgs) const {
204 
205   switch (GetCXXStdlibType(Args)) {
206   case ToolChain::CST_Libcxx:
207     CmdArgs.push_back("-lc++");
208     CmdArgs.push_back("-lc++abi");
209     break;
210   case ToolChain::CST_Libstdcxx:
211     llvm_unreachable("invalid stdlib name");
212   }
213 }
214 
215 std::string WebAssembly::getThreadModel(const ArgList &DriverArgs) const {
216   // The WebAssembly MVP does not yet support threads. We set this to "posix"
217   // when '-pthread' is set.
218   bool Pthread = false;
219   StringRef ThreadModel = "";
220   parseThreadArgs(getDriver(), DriverArgs, Pthread, ThreadModel, false);
221   if (Pthread)
222     return "posix";
223   return ThreadModel;
224 }
225 
226 Tool *WebAssembly::buildLinker() const {
227   return new tools::wasm::Linker(*this);
228 }
229