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(Ids: options::OPT_fuse_ld_EQ)) { |
41 | StringRef UseLinker = A->getValue(); |
42 | if (!UseLinker.empty()) { |
43 | if (llvm::sys::path::is_absolute(path: UseLinker) && |
44 | llvm::sys::fs::can_execute(Path: 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(Name: "wasm-ld" ); |
52 | } |
53 | |
54 | // Allow 'ld' as an alias for the default linker |
55 | if (UseLinker != "ld" ) |
56 | ToolChain.getDriver().Diag(DiagID: diag::err_drv_invalid_linker_name) |
57 | << A->getAsString(Args); |
58 | } |
59 | } |
60 | |
61 | return ToolChain.GetProgramPath(Name: 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(Str: getLinkerPath(Args)); |
72 | ArgStringList CmdArgs; |
73 | |
74 | CmdArgs.push_back(Elt: "-m" ); |
75 | if (ToolChain.getTriple().isArch64Bit()) |
76 | CmdArgs.push_back(Elt: "wasm64" ); |
77 | else |
78 | CmdArgs.push_back(Elt: "wasm32" ); |
79 | |
80 | if (Args.hasArg(Ids: options::OPT_s)) |
81 | CmdArgs.push_back(Elt: "--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(path: Linker).ends_with_insensitive( |
88 | Suffix: "wasm-component-ld" )) { |
89 | CmdArgs.push_back(Elt: "--wasm-ld-path" ); |
90 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: ToolChain.GetProgramPath(Name: "wasm-ld" ))); |
91 | } |
92 | |
93 | Args.addAllArgs(Output&: CmdArgs, Ids: {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(Ids: options::OPT_shared)) |
104 | IsCommand = false; |
105 | |
106 | if (const Arg *A = Args.getLastArg(Ids: 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(DiagID: 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(Name: "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(Ids: options::OPT_nostdlib, Ids: options::OPT_nostartfiles)) |
134 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: ToolChain.GetFilePath(Name: Crt1))); |
135 | if (Entry) { |
136 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "--entry" )); |
137 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: Entry)); |
138 | } |
139 | |
140 | if (Args.hasArg(Ids: options::OPT_shared)) |
141 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-shared" )); |
142 | |
143 | AddLinkerInputs(TC: ToolChain, Inputs, Args, CmdArgs, JA); |
144 | |
145 | if (!Args.hasArg(Ids: options::OPT_nostdlib, Ids: options::OPT_nodefaultlibs)) { |
146 | if (ToolChain.ShouldLinkCXXStdlib(Args)) |
147 | ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); |
148 | |
149 | if (Args.hasArg(Ids: options::OPT_pthread)) { |
150 | CmdArgs.push_back(Elt: "-lpthread" ); |
151 | CmdArgs.push_back(Elt: "--shared-memory" ); |
152 | } |
153 | |
154 | CmdArgs.push_back(Elt: "-lc" ); |
155 | AddRunTimeLibs(TC: ToolChain, D: ToolChain.getDriver(), CmdArgs, Args); |
156 | } |
157 | |
158 | CmdArgs.push_back(Elt: "-o" ); |
159 | CmdArgs.push_back(Elt: Output.getFilename()); |
160 | |
161 | if (Args.hasFlag(Pos: options::OPT_wasm_opt, Neg: options::OPT_no_wasm_opt, Default: true)) { |
162 | // When optimizing, if wasm-opt is available, run it. |
163 | std::string WasmOptPath; |
164 | if (Args.getLastArg(Ids: options::OPT_O_Group)) { |
165 | WasmOptPath = ToolChain.GetProgramPath(Name: "wasm-opt" ); |
166 | if (WasmOptPath == "wasm-opt" ) { |
167 | WasmOptPath = {}; |
168 | } |
169 | } |
170 | |
171 | if (!WasmOptPath.empty()) { |
172 | CmdArgs.push_back(Elt: "--keep-section=target_features" ); |
173 | } |
174 | |
175 | C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, |
176 | args: ResponseFileSupport::AtFileCurCP(), |
177 | args&: Linker, args&: CmdArgs, args: Inputs, args: Output)); |
178 | |
179 | if (Arg *A = Args.getLastArg(Ids: options::OPT_O_Group)) { |
180 | if (!WasmOptPath.empty()) { |
181 | StringRef OOpt = "s" ; |
182 | if (A->getOption().matches(ID: options::OPT_O4) || |
183 | A->getOption().matches(ID: options::OPT_Ofast)) |
184 | OOpt = "4" ; |
185 | else if (A->getOption().matches(ID: options::OPT_O0)) |
186 | OOpt = "0" ; |
187 | else if (A->getOption().matches(ID: options::OPT_O)) |
188 | OOpt = A->getValue(); |
189 | |
190 | if (OOpt != "0" ) { |
191 | const char *WasmOpt = Args.MakeArgString(Str: WasmOptPath); |
192 | ArgStringList OptArgs; |
193 | OptArgs.push_back(Elt: Output.getFilename()); |
194 | OptArgs.push_back(Elt: Args.MakeArgString(Str: llvm::Twine("-O" ) + OOpt)); |
195 | OptArgs.push_back(Elt: "-o" ); |
196 | OptArgs.push_back(Elt: Output.getFilename()); |
197 | C.addCommand(C: std::make_unique<Command>( |
198 | args: JA, args: *this, args: ResponseFileSupport::AtFileCurCP(), args&: WasmOpt, args&: OptArgs, |
199 | args: Inputs, args: 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(Elt: 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(Elt: SysRoot + "/lib" ); |
229 | } else { |
230 | const std::string MultiarchTriple = |
231 | getMultiarchTriple(D: getDriver(), TargetTriple: 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(Dir: SysRoot + "/lib/" + MultiarchTriple); |
237 | getFilePaths().push_back(Elt: Dir); |
238 | } |
239 | getFilePaths().push_back(Elt: 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(Pos: clang::driver::options::OPT_fuse_init_array, |
274 | Neg: options::OPT_fno_use_init_array, Default: true)) |
275 | CC1Args.push_back(Elt: "-fno-use-init-array" ); |
276 | |
277 | // '-pthread' implies atomics, bulk-memory, mutable-globals, and sign-ext |
278 | if (DriverArgs.hasFlag(Pos: options::OPT_pthread, Neg: options::OPT_no_pthread, |
279 | Default: false)) { |
280 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_atomics, Neg: options::OPT_matomics, |
281 | Default: false)) |
282 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
283 | << "-pthread" |
284 | << "-mno-atomics" ; |
285 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_bulk_memory, |
286 | Neg: options::OPT_mbulk_memory, Default: false)) |
287 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
288 | << "-pthread" |
289 | << "-mno-bulk-memory" ; |
290 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_mutable_globals, |
291 | Neg: options::OPT_mmutable_globals, Default: false)) |
292 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
293 | << "-pthread" |
294 | << "-mno-mutable-globals" ; |
295 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_sign_ext, Neg: options::OPT_msign_ext, |
296 | Default: false)) |
297 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
298 | << "-pthread" |
299 | << "-mno-sign-ext" ; |
300 | CC1Args.push_back(Elt: "-target-feature" ); |
301 | CC1Args.push_back(Elt: "+atomics" ); |
302 | CC1Args.push_back(Elt: "-target-feature" ); |
303 | CC1Args.push_back(Elt: "+bulk-memory" ); |
304 | CC1Args.push_back(Elt: "-target-feature" ); |
305 | CC1Args.push_back(Elt: "+mutable-globals" ); |
306 | CC1Args.push_back(Elt: "-target-feature" ); |
307 | CC1Args.push_back(Elt: "+sign-ext" ); |
308 | } |
309 | |
310 | if (!DriverArgs.hasFlag(Pos: options::OPT_mmutable_globals, |
311 | Neg: options::OPT_mno_mutable_globals, Default: 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(args&: RelocationModel, args&: PICLevel, args&: IsPIE) = |
318 | ParsePICArgs(ToolChain: *this, Args: DriverArgs); |
319 | if (RelocationModel == llvm::Reloc::PIC_) { |
320 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_mutable_globals, |
321 | Neg: options::OPT_mmutable_globals, Default: false)) { |
322 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
323 | << "-fPIC" |
324 | << "-mno-mutable-globals" ; |
325 | } |
326 | CC1Args.push_back(Elt: "-target-feature" ); |
327 | CC1Args.push_back(Elt: "+mutable-globals" ); |
328 | } |
329 | } |
330 | |
331 | if (DriverArgs.getLastArg(Ids: options::OPT_fwasm_exceptions)) { |
332 | // '-fwasm-exceptions' is not compatible with '-mno-exception-handling' |
333 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_exception_handing, |
334 | Neg: options::OPT_mexception_handing, Default: false)) |
335 | getDriver().Diag(DiagID: 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(Ids: options::OPT_mllvm)) { |
341 | if (StringRef(A->getValue(N: 0)) == "-enable-emscripten-cxx-exceptions" ) |
342 | getDriver().Diag(DiagID: 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(Elt: "-target-feature" ); |
348 | CC1Args.push_back(Elt: "+exception-handling" ); |
349 | // Backend needs -wasm-enable-eh to enable Wasm EH |
350 | CC1Args.push_back(Elt: "-mllvm" ); |
351 | CC1Args.push_back(Elt: "-wasm-enable-eh" ); |
352 | |
353 | // New Wasm EH spec (adopted in Oct 2023) requires multivalue and |
354 | // reference-types. |
355 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_multivalue, |
356 | Neg: options::OPT_mmultivalue, Default: false)) { |
357 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
358 | << "-fwasm-exceptions" << "-mno-multivalue" ; |
359 | } |
360 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_reference_types, |
361 | Neg: options::OPT_mreference_types, Default: false)) { |
362 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
363 | << "-fwasm-exceptions" << "-mno-reference-types" ; |
364 | } |
365 | CC1Args.push_back(Elt: "-target-feature" ); |
366 | CC1Args.push_back(Elt: "+multivalue" ); |
367 | CC1Args.push_back(Elt: "-target-feature" ); |
368 | CC1Args.push_back(Elt: "+reference-types" ); |
369 | } |
370 | |
371 | for (const Arg *A : DriverArgs.filtered(Ids: options::OPT_mllvm)) { |
372 | StringRef Opt = A->getValue(N: 0); |
373 | if (Opt.starts_with(Prefix: "-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(Ids: options::OPT_mllvm)) { |
378 | if (StringRef(A->getValue(N: 0)) == "-enable-emscripten-cxx-exceptions" ) { |
379 | EmEHArgExists = true; |
380 | break; |
381 | } |
382 | } |
383 | if (!EmEHArgExists) |
384 | getDriver().Diag(DiagID: 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(Separator: '=').second; |
391 | SmallVector<StringRef, 4> FuncNames; |
392 | FuncNamesStr.split(A&: FuncNames, Separator: ','); |
393 | for (auto Name : FuncNames) { |
394 | CC1Args.push_back(Elt: "-mllvm" ); |
395 | CC1Args.push_back(Elt: DriverArgs.MakeArgString(Str: "--force-attribute=" + Name + |
396 | ":noinline" )); |
397 | } |
398 | } |
399 | |
400 | if (Opt.starts_with(Prefix: "-wasm-enable-sjlj" )) { |
401 | // '-mllvm -wasm-enable-sjlj' is not compatible with |
402 | // '-mno-exception-handling' |
403 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_exception_handing, |
404 | Neg: options::OPT_mexception_handing, Default: false)) |
405 | getDriver().Diag(DiagID: 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(Ids: options::OPT_mllvm)) { |
412 | if (StringRef(A->getValue(N: 0)) == "-enable-emscripten-cxx-exceptions" ) |
413 | getDriver().Diag(DiagID: 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(Ids: options::OPT_mllvm)) { |
420 | if (StringRef(A->getValue(N: 0)) == "-enable-emscripten-sjlj" ) |
421 | getDriver().Diag(DiagID: 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(Elt: "-target-feature" ); |
427 | CC1Args.push_back(Elt: "+exception-handling" ); |
428 | // Backend needs '-exception-model=wasm' to use Wasm EH instructions |
429 | CC1Args.push_back(Elt: "-exception-model=wasm" ); |
430 | |
431 | // New Wasm EH spec (adopted in Oct 2023) requires multivalue and |
432 | // reference-types. |
433 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_multivalue, |
434 | Neg: options::OPT_mmultivalue, Default: false)) { |
435 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
436 | << "-mllvm -wasm-enable-sjlj" << "-mno-multivalue" ; |
437 | } |
438 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_reference_types, |
439 | Neg: options::OPT_mreference_types, Default: false)) { |
440 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
441 | << "-mllvm -wasm-enable-sjlj" << "-mno-reference-types" ; |
442 | } |
443 | CC1Args.push_back(Elt: "-target-feature" ); |
444 | CC1Args.push_back(Elt: "+multivalue" ); |
445 | CC1Args.push_back(Elt: "-target-feature" ); |
446 | CC1Args.push_back(Elt: "+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(Ids: 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(DiagID: 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(Ids: clang::driver::options::OPT_nostdinc)) |
473 | return; |
474 | |
475 | const Driver &D = getDriver(); |
476 | |
477 | if (!DriverArgs.hasArg(Ids: options::OPT_nobuiltininc)) { |
478 | SmallString<128> P(D.ResourceDir); |
479 | llvm::sys::path::append(path&: P, a: "include" ); |
480 | addSystemInclude(DriverArgs, CC1Args, Path: P); |
481 | } |
482 | |
483 | if (DriverArgs.hasArg(Ids: 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(A&: dirs, Separator: ":" ); |
491 | for (StringRef dir : dirs) { |
492 | StringRef Prefix = |
493 | llvm::sys::path::is_absolute(path: dir) ? "" : StringRef(D.SysRoot); |
494 | addExternCSystemInclude(DriverArgs, CC1Args, Path: Prefix + dir); |
495 | } |
496 | return; |
497 | } |
498 | |
499 | if (getTriple().getOS() != llvm::Triple::UnknownOS) { |
500 | const std::string MultiarchTriple = |
501 | getMultiarchTriple(D, TargetTriple: getTriple(), SysRoot: D.SysRoot); |
502 | addSystemInclude(DriverArgs, CC1Args, Path: D.SysRoot + "/include/" + MultiarchTriple); |
503 | } |
504 | addSystemInclude(DriverArgs, CC1Args, Path: D.SysRoot + "/include" ); |
505 | } |
506 | |
507 | void WebAssembly::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, |
508 | ArgStringList &CC1Args) const { |
509 | |
510 | if (DriverArgs.hasArg(Ids: options::OPT_nostdlibinc, Ids: options::OPT_nostdinc, |
511 | Ids: options::OPT_nostdincxx)) |
512 | return; |
513 | |
514 | switch (GetCXXStdlibType(Args: 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(Elt: "-lc++" ); |
530 | if (Args.hasArg(Ids: options::OPT_fexperimental_library)) |
531 | CmdArgs.push_back(Elt: "-lc++experimental" ); |
532 | CmdArgs.push_back(Elt: "-lc++abi" ); |
533 | break; |
534 | case ToolChain::CST_Libstdcxx: |
535 | CmdArgs.push_back(Elt: "-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, TargetTriple: getTriple(), SysRoot); |
563 | bool IsKnownOs = (getTriple().getOS() != llvm::Triple::UnknownOS); |
564 | |
565 | std::string Version = detectLibcxxVersion(IncludePath: 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, Path: TargetDir); |
573 | } |
574 | |
575 | // Second add the generic one. |
576 | addSystemInclude(DriverArgs, CC1Args, Path: 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, TargetTriple: 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(VersionText: "0.0.0" ); |
599 | SmallString<128> Path(LibPath); |
600 | llvm::sys::path::append(path&: Path, a: "c++" ); |
601 | for (llvm::vfs::directory_iterator LI = getVFS().dir_begin(Dir: Path, EC), LE; |
602 | !EC && LI != LE; LI = LI.increment(EC)) { |
603 | StringRef VersionText = llvm::sys::path::filename(path: 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, Path: TargetDir); |
621 | } |
622 | |
623 | // Second add the generic one. |
624 | addSystemInclude(DriverArgs, CC1Args, Path: LibPath + "/c++/" + Version); |
625 | // Third the backward one. |
626 | addSystemInclude(DriverArgs, CC1Args, Path: LibPath + "/c++/" + Version + "/backward" ); |
627 | } |
628 | |