| 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. |
| 30 | 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 | |
| 37 | std::string wasm::Linker::getLinkerPath(const ArgList &Args) const { |
| 38 | const ToolChain &ToolChain = getToolChain(); |
| 39 | if (const Arg* A = Args.getLastArg(Ids: options::OPT_fuse_ld_EQ)) { |
| 40 | StringRef UseLinker = A->getValue(); |
| 41 | if (!UseLinker.empty()) { |
| 42 | if (llvm::sys::path::is_absolute(path: UseLinker) && |
| 43 | llvm::sys::fs::can_execute(Path: 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(Name: "wasm-ld" ); |
| 51 | } |
| 52 | |
| 53 | // Allow 'ld' as an alias for the default linker |
| 54 | if (UseLinker != "ld" ) |
| 55 | ToolChain.getDriver().Diag(DiagID: diag::err_drv_invalid_linker_name) |
| 56 | << A->getAsString(Args); |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | return ToolChain.GetProgramPath(Name: ToolChain.getDefaultLinker()); |
| 61 | } |
| 62 | |
| 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 | |
| 70 | static bool WantsPthread(const llvm::Triple &Triple, const ArgList &Args) { |
| 71 | bool WantsPthread = |
| 72 | Args.hasFlag(Pos: options::OPT_pthread, Neg: options::OPT_no_pthread, Default: 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 | |
| 82 | 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(Str: getLinkerPath(Args)); |
| 90 | ArgStringList CmdArgs; |
| 91 | |
| 92 | CmdArgs.push_back(Elt: "-m" ); |
| 93 | if (ToolChain.getTriple().isArch64Bit()) |
| 94 | CmdArgs.push_back(Elt: "wasm64" ); |
| 95 | else |
| 96 | CmdArgs.push_back(Elt: "wasm32" ); |
| 97 | |
| 98 | if (Args.hasArg(Ids: options::OPT_s)) |
| 99 | CmdArgs.push_back(Elt: "--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(path: Linker).ends_with_insensitive( |
| 106 | Suffix: "wasm-component-ld" )) { |
| 107 | CmdArgs.push_back(Elt: "--wasm-ld-path" ); |
| 108 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: ToolChain.GetProgramPath(Name: "wasm-ld" ))); |
| 109 | } |
| 110 | |
| 111 | Args.addAllArgs(Output&: CmdArgs, Ids: {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(Ids: options::OPT_shared)) |
| 122 | IsCommand = false; |
| 123 | |
| 124 | if (const Arg *A = Args.getLastArg(Ids: 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(DiagID: 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(Name: "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(Ids: options::OPT_nostdlib, Ids: options::OPT_nostartfiles)) |
| 152 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: ToolChain.GetFilePath(Name: Crt1))); |
| 153 | if (Entry) { |
| 154 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "--entry" )); |
| 155 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: Entry)); |
| 156 | } |
| 157 | |
| 158 | if (Args.hasArg(Ids: options::OPT_shared)) |
| 159 | CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-shared" )); |
| 160 | |
| 161 | AddLinkerInputs(TC: ToolChain, Inputs, Args, CmdArgs, JA); |
| 162 | |
| 163 | if (WantsPthread(Triple: ToolChain.getTriple(), Args)) |
| 164 | CmdArgs.push_back(Elt: "--shared-memory" ); |
| 165 | |
| 166 | if (!Args.hasArg(Ids: options::OPT_nostdlib, Ids: options::OPT_nodefaultlibs)) { |
| 167 | if (ToolChain.ShouldLinkCXXStdlib(Args)) |
| 168 | ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); |
| 169 | |
| 170 | if (WantsPthread(Triple: ToolChain.getTriple(), Args)) |
| 171 | CmdArgs.push_back(Elt: "-lpthread" ); |
| 172 | |
| 173 | CmdArgs.push_back(Elt: "-lc" ); |
| 174 | AddRunTimeLibs(TC: ToolChain, D: ToolChain.getDriver(), CmdArgs, Args); |
| 175 | } |
| 176 | |
| 177 | ToolChain.addProfileRTLibs(Args, CmdArgs); |
| 178 | |
| 179 | CmdArgs.push_back(Elt: "-o" ); |
| 180 | CmdArgs.push_back(Elt: 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(TargetTriple: ToolChain.getTriple()); |
| 186 | bool RunWasmOpt = Args.hasFlag(Pos: options::OPT_wasm_opt, |
| 187 | Neg: options::OPT_no_wasm_opt, Default: 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(Ids: options::OPT_O_Group)) { |
| 193 | WasmOptPath = ToolChain.GetProgramPath(Name: "wasm-opt" ); |
| 194 | if (WasmOptPath == "wasm-opt" ) { |
| 195 | WasmOptPath = {}; |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | if (!WasmOptPath.empty()) { |
| 200 | CmdArgs.push_back(Elt: "--keep-section=target_features" ); |
| 201 | } |
| 202 | |
| 203 | C.addCommand(C: std::make_unique<Command>(args: JA, args: *this, |
| 204 | args: ResponseFileSupport::AtFileCurCP(), |
| 205 | args&: Linker, args&: CmdArgs, args: Inputs, args: Output)); |
| 206 | |
| 207 | if (Arg *A = Args.getLastArg(Ids: options::OPT_O_Group)) { |
| 208 | if (!WasmOptPath.empty()) { |
| 209 | StringRef OOpt = "s" ; |
| 210 | if (A->getOption().matches(ID: options::OPT_O4) || |
| 211 | A->getOption().matches(ID: options::OPT_Ofast)) |
| 212 | OOpt = "4" ; |
| 213 | else if (A->getOption().matches(ID: options::OPT_O0)) |
| 214 | OOpt = "0" ; |
| 215 | else if (A->getOption().matches(ID: options::OPT_O)) |
| 216 | OOpt = A->getValue(); |
| 217 | |
| 218 | if (OOpt != "0" ) { |
| 219 | const char *WasmOpt = Args.MakeArgString(Str: WasmOptPath); |
| 220 | ArgStringList OptArgs; |
| 221 | OptArgs.push_back(Elt: Output.getFilename()); |
| 222 | OptArgs.push_back(Elt: Args.MakeArgString(Str: llvm::Twine("-O" ) + OOpt)); |
| 223 | OptArgs.push_back(Elt: "-o" ); |
| 224 | OptArgs.push_back(Elt: Output.getFilename()); |
| 225 | C.addCommand(C: std::make_unique<Command>( |
| 226 | args: JA, args: *this, args: ResponseFileSupport::AtFileCurCP(), args&: WasmOpt, args&: OptArgs, |
| 227 | args: Inputs, args: Output)); |
| 228 | } |
| 229 | } |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | /// Given a base library directory, append path components to form the |
| 234 | /// LTO directory. |
| 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 | |
| 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(Elt: 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(Elt: SysRoot + "/lib" ); |
| 256 | } else { |
| 257 | const std::string MultiarchTriple = |
| 258 | getMultiarchTriple(D: getDriver(), TargetTriple: 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(Dir: SysRoot + "/lib/" + MultiarchTriple); |
| 264 | getFilePaths().push_back(Elt: Dir); |
| 265 | } |
| 266 | getFilePaths().push_back(Elt: SysRoot + "/lib/" + MultiarchTriple); |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | const char *WebAssembly::getDefaultLinker() const { |
| 271 | if (TargetBuildsComponents(TargetTriple: getTriple())) |
| 272 | return "wasm-component-ld" ; |
| 273 | return "wasm-ld" ; |
| 274 | } |
| 275 | |
| 276 | bool WebAssembly::IsMathErrnoDefault() const { return false; } |
| 277 | |
| 278 | bool WebAssembly::IsObjCNonFragileABIDefault() const { return true; } |
| 279 | |
| 280 | bool WebAssembly::UseObjCMixedDispatch() const { return true; } |
| 281 | |
| 282 | bool WebAssembly::isPICDefault() const { return false; } |
| 283 | |
| 284 | bool WebAssembly::isPIEDefault(const llvm::opt::ArgList &Args) const { |
| 285 | return false; |
| 286 | } |
| 287 | |
| 288 | bool WebAssembly::isPICDefaultForced() const { return false; } |
| 289 | |
| 290 | bool WebAssembly::hasBlocksRuntime() const { return false; } |
| 291 | |
| 292 | // TODO: Support profiling. |
| 293 | bool WebAssembly::SupportsProfiling() const { return false; } |
| 294 | |
| 295 | bool WebAssembly::HasNativeLLVMSupport() const { return true; } |
| 296 | |
| 297 | void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs, |
| 298 | ArgStringList &CC1Args, |
| 299 | Action::OffloadKind) const { |
| 300 | if (!DriverArgs.hasFlag(Pos: clang::driver::options::OPT_fuse_init_array, |
| 301 | Neg: options::OPT_fno_use_init_array, Default: true)) |
| 302 | CC1Args.push_back(Elt: "-fno-use-init-array" ); |
| 303 | |
| 304 | // '-pthread' implies atomics, bulk-memory, mutable-globals, and sign-ext |
| 305 | if (WantsPthread(Triple: getTriple(), Args: DriverArgs)) { |
| 306 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_atomics, Neg: options::OPT_matomics, |
| 307 | Default: false)) |
| 308 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
| 309 | << "-pthread" |
| 310 | << "-mno-atomics" ; |
| 311 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_bulk_memory, |
| 312 | Neg: options::OPT_mbulk_memory, Default: false)) |
| 313 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
| 314 | << "-pthread" |
| 315 | << "-mno-bulk-memory" ; |
| 316 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_mutable_globals, |
| 317 | Neg: options::OPT_mmutable_globals, Default: false)) |
| 318 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
| 319 | << "-pthread" |
| 320 | << "-mno-mutable-globals" ; |
| 321 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_sign_ext, Neg: options::OPT_msign_ext, |
| 322 | Default: false)) |
| 323 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
| 324 | << "-pthread" |
| 325 | << "-mno-sign-ext" ; |
| 326 | CC1Args.push_back(Elt: "-target-feature" ); |
| 327 | CC1Args.push_back(Elt: "+atomics" ); |
| 328 | CC1Args.push_back(Elt: "-target-feature" ); |
| 329 | CC1Args.push_back(Elt: "+bulk-memory" ); |
| 330 | CC1Args.push_back(Elt: "-target-feature" ); |
| 331 | CC1Args.push_back(Elt: "+mutable-globals" ); |
| 332 | CC1Args.push_back(Elt: "-target-feature" ); |
| 333 | CC1Args.push_back(Elt: "+sign-ext" ); |
| 334 | } |
| 335 | |
| 336 | if (!DriverArgs.hasFlag(Pos: options::OPT_mmutable_globals, |
| 337 | Neg: options::OPT_mno_mutable_globals, Default: 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(args&: RelocationModel, args&: PICLevel, args&: IsPIE) = |
| 344 | ParsePICArgs(ToolChain: *this, Args: DriverArgs); |
| 345 | if (RelocationModel == llvm::Reloc::PIC_) { |
| 346 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_mutable_globals, |
| 347 | Neg: options::OPT_mmutable_globals, Default: false)) { |
| 348 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
| 349 | << "-fPIC" |
| 350 | << "-mno-mutable-globals" ; |
| 351 | } |
| 352 | CC1Args.push_back(Elt: "-target-feature" ); |
| 353 | CC1Args.push_back(Elt: "+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(Pos: options::OPT_mno_exception_handing, |
| 367 | Neg: options::OPT_mexception_handing, Default: false)) |
| 368 | getDriver().Diag(DiagID: 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(Pos: options::OPT_mno_multivalue, |
| 372 | Neg: options::OPT_mmultivalue, Default: false)) |
| 373 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
| 374 | << CurOption << "-mno-multivalue" ; |
| 375 | if (DriverArgs.hasFlag(Pos: options::OPT_mno_reference_types, |
| 376 | Neg: options::OPT_mreference_types, Default: false)) |
| 377 | getDriver().Diag(DiagID: diag::err_drv_argument_not_allowed_with) |
| 378 | << CurOption << "-mno-reference-types" ; |
| 379 | |
| 380 | for (const Arg *A : DriverArgs.filtered(Ids: 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(N: 0)) == Option) |
| 385 | getDriver().Diag(DiagID: 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(Elt: "-target-feature" ); |
| 397 | CC1Args.push_back(Elt: "+exception-handling" ); |
| 398 | // The standardized Wasm EH spec requires multivalue and reference-types. |
| 399 | CC1Args.push_back(Elt: "-target-feature" ); |
| 400 | CC1Args.push_back(Elt: "+multivalue" ); |
| 401 | CC1Args.push_back(Elt: "-target-feature" ); |
| 402 | CC1Args.push_back(Elt: "+reference-types" ); |
| 403 | // Backend needs '-exception-model=wasm' to use Wasm EH instructions |
| 404 | CC1Args.push_back(Elt: "-exception-model=wasm" ); |
| 405 | }; |
| 406 | |
| 407 | if (DriverArgs.getLastArg(Ids: options::OPT_fwasm_exceptions)) { |
| 408 | BanIncompatibleOptionsForWasmEHSjLj("-fwasm-exceptions" ); |
| 409 | EnableFeaturesForWasmEHSjLj(); |
| 410 | // Backend needs -wasm-enable-eh to enable Wasm EH |
| 411 | CC1Args.push_back(Elt: "-mllvm" ); |
| 412 | CC1Args.push_back(Elt: "-wasm-enable-eh" ); |
| 413 | } |
| 414 | |
| 415 | for (const Arg *A : DriverArgs.filtered(Ids: options::OPT_mllvm)) { |
| 416 | StringRef Opt = A->getValue(N: 0); |
| 417 | if (Opt.starts_with(Prefix: "-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(Ids: options::OPT_mllvm)) { |
| 422 | if (StringRef(A->getValue(N: 0)) == "-enable-emscripten-cxx-exceptions" ) { |
| 423 | EmEHArgExists = true; |
| 424 | break; |
| 425 | } |
| 426 | } |
| 427 | if (!EmEHArgExists) |
| 428 | getDriver().Diag(DiagID: 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(Separator: '=').second; |
| 435 | SmallVector<StringRef, 4> FuncNames; |
| 436 | FuncNamesStr.split(A&: FuncNames, Separator: ','); |
| 437 | for (auto Name : FuncNames) { |
| 438 | CC1Args.push_back(Elt: "-mllvm" ); |
| 439 | CC1Args.push_back(Elt: DriverArgs.MakeArgString(Str: "--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(Prefix: Option)) { |
| 447 | BanIncompatibleOptionsForWasmEHSjLj(Option); |
| 448 | EnableFeaturesForWasmEHSjLj(); |
| 449 | } |
| 450 | } |
| 451 | } |
| 452 | } |
| 453 | |
| 454 | ToolChain::RuntimeLibType WebAssembly::GetDefaultRuntimeLibType() const { |
| 455 | return ToolChain::RLT_CompilerRT; |
| 456 | } |
| 457 | |
| 458 | ToolChain::CXXStdlibType |
| 459 | WebAssembly::GetCXXStdlibType(const ArgList &Args) const { |
| 460 | if (Arg *A = Args.getLastArg(Ids: 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(DiagID: diag::err_drv_invalid_stdlib_name) |
| 468 | << A->getAsString(Args); |
| 469 | } |
| 470 | return ToolChain::CST_Libcxx; |
| 471 | } |
| 472 | |
| 473 | void WebAssembly::AddClangSystemIncludeArgs(const ArgList &DriverArgs, |
| 474 | ArgStringList &CC1Args) const { |
| 475 | if (DriverArgs.hasArg(Ids: clang::driver::options::OPT_nostdinc)) |
| 476 | return; |
| 477 | |
| 478 | const Driver &D = getDriver(); |
| 479 | |
| 480 | if (!DriverArgs.hasArg(Ids: options::OPT_nobuiltininc)) { |
| 481 | SmallString<128> P(D.ResourceDir); |
| 482 | llvm::sys::path::append(path&: P, a: "include" ); |
| 483 | addSystemInclude(DriverArgs, CC1Args, Path: P); |
| 484 | } |
| 485 | |
| 486 | if (DriverArgs.hasArg(Ids: 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(A&: dirs, Separator: ":" ); |
| 494 | for (StringRef dir : dirs) { |
| 495 | StringRef Prefix = |
| 496 | llvm::sys::path::is_absolute(path: dir) ? "" : StringRef(D.SysRoot); |
| 497 | addExternCSystemInclude(DriverArgs, CC1Args, Path: Prefix + dir); |
| 498 | } |
| 499 | return; |
| 500 | } |
| 501 | |
| 502 | if (getTriple().getOS() != llvm::Triple::UnknownOS) { |
| 503 | const std::string MultiarchTriple = |
| 504 | getMultiarchTriple(D, TargetTriple: getTriple(), SysRoot: D.SysRoot); |
| 505 | addSystemInclude(DriverArgs, CC1Args, Path: D.SysRoot + "/include/" + MultiarchTriple); |
| 506 | } |
| 507 | addSystemInclude(DriverArgs, CC1Args, Path: D.SysRoot + "/include" ); |
| 508 | } |
| 509 | |
| 510 | void WebAssembly::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, |
| 511 | ArgStringList &CC1Args) const { |
| 512 | |
| 513 | if (DriverArgs.hasArg(Ids: options::OPT_nostdlibinc, Ids: options::OPT_nostdinc, |
| 514 | Ids: options::OPT_nostdincxx)) |
| 515 | return; |
| 516 | |
| 517 | switch (GetCXXStdlibType(Args: 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 | |
| 527 | 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(Elt: "-lc++" ); |
| 533 | if (Args.hasArg(Ids: options::OPT_fexperimental_library)) |
| 534 | CmdArgs.push_back(Elt: "-lc++experimental" ); |
| 535 | CmdArgs.push_back(Elt: "-lc++abi" ); |
| 536 | break; |
| 537 | case ToolChain::CST_Libstdcxx: |
| 538 | CmdArgs.push_back(Elt: "-lstdc++" ); |
| 539 | break; |
| 540 | } |
| 541 | } |
| 542 | |
| 543 | 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 | |
| 559 | Tool *WebAssembly::buildLinker() const { |
| 560 | return new tools::wasm::Linker(*this); |
| 561 | } |
| 562 | |
| 563 | 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, TargetTriple: getTriple(), SysRoot); |
| 571 | bool IsKnownOs = (getTriple().getOS() != llvm::Triple::UnknownOS); |
| 572 | |
| 573 | std::string Version = detectLibcxxVersion(IncludePath: 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, Path: TargetDir); |
| 581 | } |
| 582 | |
| 583 | // Second add the generic one. |
| 584 | addSystemInclude(DriverArgs, CC1Args, Path: LibPath + "/c++/" + Version); |
| 585 | } |
| 586 | |
| 587 | 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, TargetTriple: 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(VersionText: "0.0.0" ); |
| 607 | SmallString<128> Path(LibPath); |
| 608 | llvm::sys::path::append(path&: Path, a: "c++" ); |
| 609 | for (llvm::vfs::directory_iterator LI = getVFS().dir_begin(Dir: Path, EC), LE; |
| 610 | !EC && LI != LE; LI = LI.increment(EC)) { |
| 611 | StringRef VersionText = llvm::sys::path::filename(path: 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, Path: TargetDir); |
| 629 | } |
| 630 | |
| 631 | // Second add the generic one. |
| 632 | addSystemInclude(DriverArgs, CC1Args, Path: LibPath + "/c++/" + Version); |
| 633 | // Third the backward one. |
| 634 | addSystemInclude(DriverArgs, CC1Args, Path: LibPath + "/c++/" + Version + "/backward" ); |
| 635 | } |
| 636 | |