WebAssembly.cpp revision 360784
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/Basic/Version.h"
12#include "clang/Config/config.h"
13#include "clang/Driver/Compilation.h"
14#include "clang/Driver/Driver.h"
15#include "clang/Driver/DriverDiagnostic.h"
16#include "clang/Driver/Options.h"
17#include "llvm/Support/FileSystem.h"
18#include "llvm/Support/Path.h"
19#include "llvm/Option/ArgList.h"
20
21using namespace clang::driver;
22using namespace clang::driver::tools;
23using namespace clang::driver::toolchains;
24using namespace clang;
25using namespace llvm::opt;
26
27/// Following the conventions in https://wiki.debian.org/Multiarch/Tuples,
28/// we remove the vendor field to form the multiarch triple.
29static std::string getMultiarchTriple(const Driver &D,
30                                      const llvm::Triple &TargetTriple,
31                                      StringRef SysRoot) {
32    return (TargetTriple.getArchName() + "-" +
33            TargetTriple.getOSAndEnvironmentName()).str();
34}
35
36std::string wasm::Linker::getLinkerPath(const ArgList &Args) const {
37  const ToolChain &ToolChain = getToolChain();
38  if (const Arg* A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
39    StringRef UseLinker = A->getValue();
40    if (!UseLinker.empty()) {
41      if (llvm::sys::path::is_absolute(UseLinker) &&
42          llvm::sys::fs::can_execute(UseLinker))
43        return UseLinker;
44
45      // Accept 'lld', and 'ld' as aliases for the default linker
46      if (UseLinker != "lld" && UseLinker != "ld")
47        ToolChain.getDriver().Diag(diag::err_drv_invalid_linker_name)
48            << A->getAsString(Args);
49    }
50  }
51
52  return ToolChain.GetProgramPath(ToolChain.getDefaultLinker());
53}
54
55void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
56                                const InputInfo &Output,
57                                const InputInfoList &Inputs,
58                                const ArgList &Args,
59                                const char *LinkingOutput) const {
60
61  const ToolChain &ToolChain = getToolChain();
62  const char *Linker = Args.MakeArgString(getLinkerPath(Args));
63  ArgStringList CmdArgs;
64
65  if (Args.hasArg(options::OPT_s))
66    CmdArgs.push_back("--strip-all");
67
68  Args.AddAllArgs(CmdArgs, options::OPT_L);
69  Args.AddAllArgs(CmdArgs, options::OPT_u);
70  ToolChain.AddFilePathLibArgs(Args, CmdArgs);
71
72  if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles))
73    CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crt1.o")));
74
75  AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
76
77  if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
78    if (ToolChain.ShouldLinkCXXStdlib(Args))
79      ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
80
81    if (Args.hasArg(options::OPT_pthread)) {
82      CmdArgs.push_back("-lpthread");
83      CmdArgs.push_back("--shared-memory");
84    }
85
86    CmdArgs.push_back("-lc");
87    AddRunTimeLibs(ToolChain, ToolChain.getDriver(), CmdArgs, Args);
88  }
89
90  CmdArgs.push_back("-o");
91  CmdArgs.push_back(Output.getFilename());
92
93  C.addCommand(std::make_unique<Command>(JA, *this, Linker, CmdArgs, Inputs));
94
95  // When optimizing, if wasm-opt is available, run it.
96  if (Arg *A = Args.getLastArg(options::OPT_O_Group)) {
97    auto WasmOptPath = getToolChain().GetProgramPath("wasm-opt");
98    if (WasmOptPath != "wasm-opt") {
99      StringRef OOpt = "s";
100      if (A->getOption().matches(options::OPT_O4) ||
101          A->getOption().matches(options::OPT_Ofast))
102        OOpt = "4";
103      else if (A->getOption().matches(options::OPT_O0))
104        OOpt = "0";
105      else if (A->getOption().matches(options::OPT_O))
106        OOpt = A->getValue();
107
108      if (OOpt != "0") {
109        const char *WasmOpt = Args.MakeArgString(WasmOptPath);
110        ArgStringList CmdArgs;
111        CmdArgs.push_back(Output.getFilename());
112        CmdArgs.push_back(Args.MakeArgString(llvm::Twine("-O") + OOpt));
113        CmdArgs.push_back("-o");
114        CmdArgs.push_back(Output.getFilename());
115        C.addCommand(std::make_unique<Command>(JA, *this, WasmOpt, CmdArgs, Inputs));
116      }
117    }
118  }
119}
120
121/// Given a base library directory, append path components to form the
122/// LTO directory.
123static std::string AppendLTOLibDir(const std::string &Dir) {
124    // The version allows the path to be keyed to the specific version of
125    // LLVM in used, as the bitcode format is not stable.
126    return Dir + "/llvm-lto/" LLVM_VERSION_STRING;
127}
128
129WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple,
130                         const llvm::opt::ArgList &Args)
131    : ToolChain(D, Triple, Args) {
132
133  assert(Triple.isArch32Bit() != Triple.isArch64Bit());
134
135  getProgramPaths().push_back(getDriver().getInstalledDir());
136
137  auto SysRoot = getDriver().SysRoot;
138  if (getTriple().getOS() == llvm::Triple::UnknownOS) {
139    // Theoretically an "unknown" OS should mean no standard libraries, however
140    // it could also mean that a custom set of libraries is in use, so just add
141    // /lib to the search path. Disable multiarch in this case, to discourage
142    // paths containing "unknown" from acquiring meanings.
143    getFilePaths().push_back(SysRoot + "/lib");
144  } else {
145    const std::string MultiarchTriple =
146        getMultiarchTriple(getDriver(), Triple, SysRoot);
147    if (D.isUsingLTO()) {
148      // For LTO, enable use of lto-enabled sysroot libraries too, if available.
149      // Note that the directory is keyed to the LLVM revision, as LLVM's
150      // bitcode format is not stable.
151      auto Dir = AppendLTOLibDir(SysRoot + "/lib/" + MultiarchTriple);
152      getFilePaths().push_back(Dir);
153    }
154    getFilePaths().push_back(SysRoot + "/lib/" + MultiarchTriple);
155  }
156}
157
158bool WebAssembly::IsMathErrnoDefault() const { return false; }
159
160bool WebAssembly::IsObjCNonFragileABIDefault() const { return true; }
161
162bool WebAssembly::UseObjCMixedDispatch() const { return true; }
163
164bool WebAssembly::isPICDefault() const { return false; }
165
166bool WebAssembly::isPIEDefault() const { return false; }
167
168bool WebAssembly::isPICDefaultForced() const { return false; }
169
170bool WebAssembly::IsIntegratedAssemblerDefault() const { return true; }
171
172bool WebAssembly::hasBlocksRuntime() const { return false; }
173
174// TODO: Support profiling.
175bool WebAssembly::SupportsProfiling() const { return false; }
176
177bool WebAssembly::HasNativeLLVMSupport() const { return true; }
178
179void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs,
180                                        ArgStringList &CC1Args,
181                                        Action::OffloadKind) const {
182  if (!DriverArgs.hasFlag(clang::driver::options::OPT_fuse_init_array,
183                          options::OPT_fno_use_init_array, true))
184    CC1Args.push_back("-fno-use-init-array");
185
186  // '-pthread' implies atomics, bulk-memory, mutable-globals, and sign-ext
187  if (DriverArgs.hasFlag(options::OPT_pthread, options::OPT_no_pthread,
188                         false)) {
189    if (DriverArgs.hasFlag(options::OPT_mno_atomics, options::OPT_matomics,
190                           false))
191      getDriver().Diag(diag::err_drv_argument_not_allowed_with)
192          << "-pthread"
193          << "-mno-atomics";
194    if (DriverArgs.hasFlag(options::OPT_mno_bulk_memory,
195                           options::OPT_mbulk_memory, false))
196      getDriver().Diag(diag::err_drv_argument_not_allowed_with)
197          << "-pthread"
198          << "-mno-bulk-memory";
199    if (DriverArgs.hasFlag(options::OPT_mno_mutable_globals,
200                           options::OPT_mmutable_globals, false))
201      getDriver().Diag(diag::err_drv_argument_not_allowed_with)
202          << "-pthread"
203          << "-mno-mutable-globals";
204    if (DriverArgs.hasFlag(options::OPT_mno_sign_ext, options::OPT_msign_ext,
205                           false))
206      getDriver().Diag(diag::err_drv_argument_not_allowed_with)
207          << "-pthread"
208          << "-mno-sign-ext";
209    CC1Args.push_back("-target-feature");
210    CC1Args.push_back("+atomics");
211    CC1Args.push_back("-target-feature");
212    CC1Args.push_back("+bulk-memory");
213    CC1Args.push_back("-target-feature");
214    CC1Args.push_back("+mutable-globals");
215    CC1Args.push_back("-target-feature");
216    CC1Args.push_back("+sign-ext");
217  }
218
219  if (DriverArgs.getLastArg(options::OPT_fwasm_exceptions)) {
220    // '-fwasm-exceptions' is not compatible with '-mno-exception-handling'
221    if (DriverArgs.hasFlag(options::OPT_mno_exception_handing,
222                           options::OPT_mexception_handing, false))
223      getDriver().Diag(diag::err_drv_argument_not_allowed_with)
224          << "-fwasm-exceptions"
225          << "-mno-exception-handling";
226    // '-fwasm-exceptions' is not compatible with '-mno-reference-types'
227    if (DriverArgs.hasFlag(options::OPT_mno_reference_types,
228                           options::OPT_mexception_handing, false))
229      getDriver().Diag(diag::err_drv_argument_not_allowed_with)
230          << "-fwasm-exceptions"
231          << "-mno-reference-types";
232    // '-fwasm-exceptions' is not compatible with
233    // '-mllvm -enable-emscripten-cxx-exceptions'
234    for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) {
235      if (StringRef(A->getValue(0)) == "-enable-emscripten-cxx-exceptions")
236        getDriver().Diag(diag::err_drv_argument_not_allowed_with)
237            << "-fwasm-exceptions"
238            << "-mllvm -enable-emscripten-cxx-exceptions";
239    }
240    // '-fwasm-exceptions' implies exception-handling and reference-types
241    CC1Args.push_back("-target-feature");
242    CC1Args.push_back("+exception-handling");
243    CC1Args.push_back("-target-feature");
244    CC1Args.push_back("+reference-types");
245  }
246}
247
248ToolChain::RuntimeLibType WebAssembly::GetDefaultRuntimeLibType() const {
249  return ToolChain::RLT_CompilerRT;
250}
251
252ToolChain::CXXStdlibType
253WebAssembly::GetCXXStdlibType(const ArgList &Args) const {
254  if (Arg *A = Args.getLastArg(options::OPT_stdlib_EQ)) {
255    StringRef Value = A->getValue();
256    if (Value != "libc++")
257      getDriver().Diag(diag::err_drv_invalid_stdlib_name)
258          << A->getAsString(Args);
259  }
260  return ToolChain::CST_Libcxx;
261}
262
263void WebAssembly::AddClangSystemIncludeArgs(const ArgList &DriverArgs,
264                                            ArgStringList &CC1Args) const {
265  if (DriverArgs.hasArg(clang::driver::options::OPT_nostdinc))
266    return;
267
268  const Driver &D = getDriver();
269
270  if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) {
271    SmallString<128> P(D.ResourceDir);
272    llvm::sys::path::append(P, "include");
273    addSystemInclude(DriverArgs, CC1Args, P);
274  }
275
276  if (DriverArgs.hasArg(options::OPT_nostdlibinc))
277    return;
278
279  // Check for configure-time C include directories.
280  StringRef CIncludeDirs(C_INCLUDE_DIRS);
281  if (CIncludeDirs != "") {
282    SmallVector<StringRef, 5> dirs;
283    CIncludeDirs.split(dirs, ":");
284    for (StringRef dir : dirs) {
285      StringRef Prefix =
286          llvm::sys::path::is_absolute(dir) ? StringRef(D.SysRoot) : "";
287      addExternCSystemInclude(DriverArgs, CC1Args, Prefix + dir);
288    }
289    return;
290  }
291
292  if (getTriple().getOS() != llvm::Triple::UnknownOS) {
293    const std::string MultiarchTriple =
294        getMultiarchTriple(D, getTriple(), D.SysRoot);
295    addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include/" + MultiarchTriple);
296  }
297  addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include");
298}
299
300void WebAssembly::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs,
301                                               ArgStringList &CC1Args) const {
302  if (!DriverArgs.hasArg(options::OPT_nostdlibinc) &&
303      !DriverArgs.hasArg(options::OPT_nostdincxx)) {
304    if (getTriple().getOS() != llvm::Triple::UnknownOS) {
305      const std::string MultiarchTriple =
306          getMultiarchTriple(getDriver(), getTriple(), getDriver().SysRoot);
307      addSystemInclude(DriverArgs, CC1Args,
308                       getDriver().SysRoot + "/include/" + MultiarchTriple +
309                           "/c++/v1");
310    }
311    addSystemInclude(DriverArgs, CC1Args,
312                     getDriver().SysRoot + "/include/c++/v1");
313  }
314}
315
316void WebAssembly::AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args,
317                                      llvm::opt::ArgStringList &CmdArgs) const {
318
319  switch (GetCXXStdlibType(Args)) {
320  case ToolChain::CST_Libcxx:
321    CmdArgs.push_back("-lc++");
322    CmdArgs.push_back("-lc++abi");
323    break;
324  case ToolChain::CST_Libstdcxx:
325    llvm_unreachable("invalid stdlib name");
326  }
327}
328
329SanitizerMask WebAssembly::getSupportedSanitizers() const {
330  SanitizerMask Res = ToolChain::getSupportedSanitizers();
331  if (getTriple().isOSEmscripten()) {
332    Res |= SanitizerKind::Vptr | SanitizerKind::Leak | SanitizerKind::Address;
333  }
334  return Res;
335}
336
337Tool *WebAssembly::buildLinker() const {
338  return new tools::wasm::Linker(*this);
339}
340