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(options::OPT_fuse_ld_EQ)) { 41 StringRef UseLinker = A->getValue(); 42 if (!UseLinker.empty()) { 43 if (llvm::sys::path::is_absolute(UseLinker) && 44 llvm::sys::fs::can_execute(UseLinker)) 45 return std::string(UseLinker); 46 47 // Accept 'lld', and 'ld' as aliases for the default linker 48 if (UseLinker != "lld" && UseLinker != "ld") 49 ToolChain.getDriver().Diag(diag::err_drv_invalid_linker_name) 50 << A->getAsString(Args); 51 } 52 } 53 54 return ToolChain.GetProgramPath(ToolChain.getDefaultLinker()); 55 } 56 57 void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA, 58 const InputInfo &Output, 59 const InputInfoList &Inputs, 60 const ArgList &Args, 61 const char *LinkingOutput) const { 62 63 const ToolChain &ToolChain = getToolChain(); 64 const char *Linker = Args.MakeArgString(getLinkerPath(Args)); 65 ArgStringList CmdArgs; 66 67 CmdArgs.push_back("-m"); 68 if (ToolChain.getTriple().isArch64Bit()) 69 CmdArgs.push_back("wasm64"); 70 else 71 CmdArgs.push_back("wasm32"); 72 73 if (Args.hasArg(options::OPT_s)) 74 CmdArgs.push_back("--strip-all"); 75 76 Args.AddAllArgs(CmdArgs, options::OPT_L); 77 Args.AddAllArgs(CmdArgs, options::OPT_u); 78 ToolChain.AddFilePathLibArgs(Args, CmdArgs); 79 80 const char *Crt1 = "crt1.o"; 81 const char *Entry = nullptr; 82 83 // If crt1-command.o exists, it supports new-style commands, so use it. 84 // Otherwise, use the old crt1.o. This is a temporary transition measure. 85 // Once WASI libc no longer needs to support LLVM versions which lack 86 // support for new-style command, it can make crt1.o the same as 87 // crt1-command.o. And once LLVM no longer needs to support WASI libc 88 // versions before that, it can switch to using crt1-command.o. 89 if (ToolChain.GetFilePath("crt1-command.o") != "crt1-command.o") 90 Crt1 = "crt1-command.o"; 91 92 if (const Arg *A = Args.getLastArg(options::OPT_mexec_model_EQ)) { 93 StringRef CM = A->getValue(); 94 if (CM == "command") { 95 // Use default values. 96 } else if (CM == "reactor") { 97 Crt1 = "crt1-reactor.o"; 98 Entry = "_initialize"; 99 } else { 100 ToolChain.getDriver().Diag(diag::err_drv_invalid_argument_to_option) 101 << CM << A->getOption().getName(); 102 } 103 } 104 if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles, options::OPT_shared)) 105 CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(Crt1))); 106 if (Entry) { 107 CmdArgs.push_back(Args.MakeArgString("--entry")); 108 CmdArgs.push_back(Args.MakeArgString(Entry)); 109 } 110 111 if (Args.hasArg(options::OPT_shared)) 112 CmdArgs.push_back(Args.MakeArgString("-shared")); 113 114 AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA); 115 116 if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) { 117 if (ToolChain.ShouldLinkCXXStdlib(Args)) 118 ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs); 119 120 if (Args.hasArg(options::OPT_pthread)) { 121 CmdArgs.push_back("-lpthread"); 122 CmdArgs.push_back("--shared-memory"); 123 } 124 125 CmdArgs.push_back("-lc"); 126 AddRunTimeLibs(ToolChain, ToolChain.getDriver(), CmdArgs, Args); 127 } 128 129 CmdArgs.push_back("-o"); 130 CmdArgs.push_back(Output.getFilename()); 131 132 C.addCommand(std::make_unique<Command>(JA, *this, 133 ResponseFileSupport::AtFileCurCP(), 134 Linker, CmdArgs, Inputs, Output)); 135 136 // When optimizing, if wasm-opt is available, run it. 137 if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { 138 auto WasmOptPath = ToolChain.GetProgramPath("wasm-opt"); 139 if (WasmOptPath != "wasm-opt") { 140 StringRef OOpt = "s"; 141 if (A->getOption().matches(options::OPT_O4) || 142 A->getOption().matches(options::OPT_Ofast)) 143 OOpt = "4"; 144 else if (A->getOption().matches(options::OPT_O0)) 145 OOpt = "0"; 146 else if (A->getOption().matches(options::OPT_O)) 147 OOpt = A->getValue(); 148 149 if (OOpt != "0") { 150 const char *WasmOpt = Args.MakeArgString(WasmOptPath); 151 ArgStringList CmdArgs; 152 CmdArgs.push_back(Output.getFilename()); 153 CmdArgs.push_back(Args.MakeArgString(llvm::Twine("-O") + OOpt)); 154 CmdArgs.push_back("-o"); 155 CmdArgs.push_back(Output.getFilename()); 156 C.addCommand(std::make_unique<Command>( 157 JA, *this, ResponseFileSupport::AtFileCurCP(), WasmOpt, CmdArgs, 158 Inputs, Output)); 159 } 160 } 161 } 162 } 163 164 /// Given a base library directory, append path components to form the 165 /// LTO directory. 166 static std::string AppendLTOLibDir(const std::string &Dir) { 167 // The version allows the path to be keyed to the specific version of 168 // LLVM in used, as the bitcode format is not stable. 169 return Dir + "/llvm-lto/" LLVM_VERSION_STRING; 170 } 171 172 WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple, 173 const llvm::opt::ArgList &Args) 174 : ToolChain(D, Triple, Args) { 175 176 assert(Triple.isArch32Bit() != Triple.isArch64Bit()); 177 178 getProgramPaths().push_back(getDriver().getInstalledDir()); 179 180 auto SysRoot = getDriver().SysRoot; 181 if (getTriple().getOS() == llvm::Triple::UnknownOS) { 182 // Theoretically an "unknown" OS should mean no standard libraries, however 183 // it could also mean that a custom set of libraries is in use, so just add 184 // /lib to the search path. Disable multiarch in this case, to discourage 185 // paths containing "unknown" from acquiring meanings. 186 getFilePaths().push_back(SysRoot + "/lib"); 187 } else { 188 const std::string MultiarchTriple = 189 getMultiarchTriple(getDriver(), Triple, SysRoot); 190 if (D.isUsingLTO()) { 191 // For LTO, enable use of lto-enabled sysroot libraries too, if available. 192 // Note that the directory is keyed to the LLVM revision, as LLVM's 193 // bitcode format is not stable. 194 auto Dir = AppendLTOLibDir(SysRoot + "/lib/" + MultiarchTriple); 195 getFilePaths().push_back(Dir); 196 } 197 getFilePaths().push_back(SysRoot + "/lib/" + MultiarchTriple); 198 } 199 } 200 201 bool WebAssembly::IsMathErrnoDefault() const { return false; } 202 203 bool WebAssembly::IsObjCNonFragileABIDefault() const { return true; } 204 205 bool WebAssembly::UseObjCMixedDispatch() const { return true; } 206 207 bool WebAssembly::isPICDefault() const { return false; } 208 209 bool WebAssembly::isPIEDefault(const llvm::opt::ArgList &Args) const { 210 return false; 211 } 212 213 bool WebAssembly::isPICDefaultForced() const { return false; } 214 215 bool WebAssembly::hasBlocksRuntime() const { return false; } 216 217 // TODO: Support profiling. 218 bool WebAssembly::SupportsProfiling() const { return false; } 219 220 bool WebAssembly::HasNativeLLVMSupport() const { return true; } 221 222 void WebAssembly::addClangTargetOptions(const ArgList &DriverArgs, 223 ArgStringList &CC1Args, 224 Action::OffloadKind) const { 225 if (!DriverArgs.hasFlag(clang::driver::options::OPT_fuse_init_array, 226 options::OPT_fno_use_init_array, true)) 227 CC1Args.push_back("-fno-use-init-array"); 228 229 // '-pthread' implies atomics, bulk-memory, mutable-globals, and sign-ext 230 if (DriverArgs.hasFlag(options::OPT_pthread, options::OPT_no_pthread, 231 false)) { 232 if (DriverArgs.hasFlag(options::OPT_mno_atomics, options::OPT_matomics, 233 false)) 234 getDriver().Diag(diag::err_drv_argument_not_allowed_with) 235 << "-pthread" 236 << "-mno-atomics"; 237 if (DriverArgs.hasFlag(options::OPT_mno_bulk_memory, 238 options::OPT_mbulk_memory, false)) 239 getDriver().Diag(diag::err_drv_argument_not_allowed_with) 240 << "-pthread" 241 << "-mno-bulk-memory"; 242 if (DriverArgs.hasFlag(options::OPT_mno_mutable_globals, 243 options::OPT_mmutable_globals, false)) 244 getDriver().Diag(diag::err_drv_argument_not_allowed_with) 245 << "-pthread" 246 << "-mno-mutable-globals"; 247 if (DriverArgs.hasFlag(options::OPT_mno_sign_ext, options::OPT_msign_ext, 248 false)) 249 getDriver().Diag(diag::err_drv_argument_not_allowed_with) 250 << "-pthread" 251 << "-mno-sign-ext"; 252 CC1Args.push_back("-target-feature"); 253 CC1Args.push_back("+atomics"); 254 CC1Args.push_back("-target-feature"); 255 CC1Args.push_back("+bulk-memory"); 256 CC1Args.push_back("-target-feature"); 257 CC1Args.push_back("+mutable-globals"); 258 CC1Args.push_back("-target-feature"); 259 CC1Args.push_back("+sign-ext"); 260 } 261 262 if (!DriverArgs.hasFlag(options::OPT_mmutable_globals, 263 options::OPT_mno_mutable_globals, false)) { 264 // -fPIC implies +mutable-globals because the PIC ABI used by the linker 265 // depends on importing and exporting mutable globals. 266 llvm::Reloc::Model RelocationModel; 267 unsigned PICLevel; 268 bool IsPIE; 269 std::tie(RelocationModel, PICLevel, IsPIE) = 270 ParsePICArgs(*this, DriverArgs); 271 if (RelocationModel == llvm::Reloc::PIC_) { 272 if (DriverArgs.hasFlag(options::OPT_mno_mutable_globals, 273 options::OPT_mmutable_globals, false)) { 274 getDriver().Diag(diag::err_drv_argument_not_allowed_with) 275 << "-fPIC" 276 << "-mno-mutable-globals"; 277 } 278 CC1Args.push_back("-target-feature"); 279 CC1Args.push_back("+mutable-globals"); 280 } 281 } 282 283 if (DriverArgs.getLastArg(options::OPT_fwasm_exceptions)) { 284 // '-fwasm-exceptions' is not compatible with '-mno-exception-handling' 285 if (DriverArgs.hasFlag(options::OPT_mno_exception_handing, 286 options::OPT_mexception_handing, false)) 287 getDriver().Diag(diag::err_drv_argument_not_allowed_with) 288 << "-fwasm-exceptions" 289 << "-mno-exception-handling"; 290 // '-fwasm-exceptions' is not compatible with 291 // '-mllvm -enable-emscripten-cxx-exceptions' 292 for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) { 293 if (StringRef(A->getValue(0)) == "-enable-emscripten-cxx-exceptions") 294 getDriver().Diag(diag::err_drv_argument_not_allowed_with) 295 << "-fwasm-exceptions" 296 << "-mllvm -enable-emscripten-cxx-exceptions"; 297 } 298 // '-fwasm-exceptions' implies exception-handling feature 299 CC1Args.push_back("-target-feature"); 300 CC1Args.push_back("+exception-handling"); 301 // Backend needs -wasm-enable-eh to enable Wasm EH 302 CC1Args.push_back("-mllvm"); 303 CC1Args.push_back("-wasm-enable-eh"); 304 } 305 306 for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) { 307 StringRef Opt = A->getValue(0); 308 if (Opt.startswith("-emscripten-cxx-exceptions-allowed")) { 309 // '-mllvm -emscripten-cxx-exceptions-allowed' should be used with 310 // '-mllvm -enable-emscripten-cxx-exceptions' 311 bool EmEHArgExists = false; 312 for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) { 313 if (StringRef(A->getValue(0)) == "-enable-emscripten-cxx-exceptions") { 314 EmEHArgExists = true; 315 break; 316 } 317 } 318 if (!EmEHArgExists) 319 getDriver().Diag(diag::err_drv_argument_only_allowed_with) 320 << "-mllvm -emscripten-cxx-exceptions-allowed" 321 << "-mllvm -enable-emscripten-cxx-exceptions"; 322 323 // Prevent functions specified in -emscripten-cxx-exceptions-allowed list 324 // from being inlined before reaching the wasm backend. 325 StringRef FuncNamesStr = Opt.split('=').second; 326 SmallVector<StringRef, 4> FuncNames; 327 FuncNamesStr.split(FuncNames, ','); 328 for (auto Name : FuncNames) { 329 CC1Args.push_back("-mllvm"); 330 CC1Args.push_back(DriverArgs.MakeArgString("--force-attribute=" + Name + 331 ":noinline")); 332 } 333 } 334 335 if (Opt.startswith("-wasm-enable-sjlj")) { 336 // '-mllvm -wasm-enable-sjlj' is not compatible with 337 // '-mno-exception-handling' 338 if (DriverArgs.hasFlag(options::OPT_mno_exception_handing, 339 options::OPT_mexception_handing, false)) 340 getDriver().Diag(diag::err_drv_argument_not_allowed_with) 341 << "-mllvm -wasm-enable-sjlj" 342 << "-mno-exception-handling"; 343 // '-mllvm -wasm-enable-sjlj' is not compatible with 344 // '-mllvm -enable-emscripten-cxx-exceptions' 345 // because we don't allow Emscripten EH + Wasm SjLj 346 for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) { 347 if (StringRef(A->getValue(0)) == "-enable-emscripten-cxx-exceptions") 348 getDriver().Diag(diag::err_drv_argument_not_allowed_with) 349 << "-mllvm -wasm-enable-sjlj" 350 << "-mllvm -enable-emscripten-cxx-exceptions"; 351 } 352 // '-mllvm -wasm-enable-sjlj' is not compatible with 353 // '-mllvm -enable-emscripten-sjlj' 354 for (const Arg *A : DriverArgs.filtered(options::OPT_mllvm)) { 355 if (StringRef(A->getValue(0)) == "-enable-emscripten-sjlj") 356 getDriver().Diag(diag::err_drv_argument_not_allowed_with) 357 << "-mllvm -wasm-enable-sjlj" 358 << "-mllvm -enable-emscripten-sjlj"; 359 } 360 // '-mllvm -wasm-enable-sjlj' implies exception-handling feature 361 CC1Args.push_back("-target-feature"); 362 CC1Args.push_back("+exception-handling"); 363 // Backend needs '-exception-model=wasm' to use Wasm EH instructions 364 CC1Args.push_back("-exception-model=wasm"); 365 } 366 } 367 } 368 369 ToolChain::RuntimeLibType WebAssembly::GetDefaultRuntimeLibType() const { 370 return ToolChain::RLT_CompilerRT; 371 } 372 373 ToolChain::CXXStdlibType 374 WebAssembly::GetCXXStdlibType(const ArgList &Args) const { 375 if (Arg *A = Args.getLastArg(options::OPT_stdlib_EQ)) { 376 StringRef Value = A->getValue(); 377 if (Value == "libc++") 378 return ToolChain::CST_Libcxx; 379 else if (Value == "libstdc++") 380 return ToolChain::CST_Libstdcxx; 381 else 382 getDriver().Diag(diag::err_drv_invalid_stdlib_name) 383 << A->getAsString(Args); 384 } 385 return ToolChain::CST_Libcxx; 386 } 387 388 void WebAssembly::AddClangSystemIncludeArgs(const ArgList &DriverArgs, 389 ArgStringList &CC1Args) const { 390 if (DriverArgs.hasArg(clang::driver::options::OPT_nostdinc)) 391 return; 392 393 const Driver &D = getDriver(); 394 395 if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { 396 SmallString<128> P(D.ResourceDir); 397 llvm::sys::path::append(P, "include"); 398 addSystemInclude(DriverArgs, CC1Args, P); 399 } 400 401 if (DriverArgs.hasArg(options::OPT_nostdlibinc)) 402 return; 403 404 // Check for configure-time C include directories. 405 StringRef CIncludeDirs(C_INCLUDE_DIRS); 406 if (CIncludeDirs != "") { 407 SmallVector<StringRef, 5> dirs; 408 CIncludeDirs.split(dirs, ":"); 409 for (StringRef dir : dirs) { 410 StringRef Prefix = 411 llvm::sys::path::is_absolute(dir) ? "" : StringRef(D.SysRoot); 412 addExternCSystemInclude(DriverArgs, CC1Args, Prefix + dir); 413 } 414 return; 415 } 416 417 if (getTriple().getOS() != llvm::Triple::UnknownOS) { 418 const std::string MultiarchTriple = 419 getMultiarchTriple(D, getTriple(), D.SysRoot); 420 addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include/" + MultiarchTriple); 421 } 422 addSystemInclude(DriverArgs, CC1Args, D.SysRoot + "/include"); 423 } 424 425 void WebAssembly::AddClangCXXStdlibIncludeArgs(const ArgList &DriverArgs, 426 ArgStringList &CC1Args) const { 427 428 if (DriverArgs.hasArg(options::OPT_nostdlibinc, options::OPT_nostdinc, 429 options::OPT_nostdincxx)) 430 return; 431 432 switch (GetCXXStdlibType(DriverArgs)) { 433 case ToolChain::CST_Libcxx: 434 addLibCxxIncludePaths(DriverArgs, CC1Args); 435 break; 436 case ToolChain::CST_Libstdcxx: 437 addLibStdCXXIncludePaths(DriverArgs, CC1Args); 438 break; 439 } 440 } 441 442 void WebAssembly::AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args, 443 llvm::opt::ArgStringList &CmdArgs) const { 444 445 switch (GetCXXStdlibType(Args)) { 446 case ToolChain::CST_Libcxx: 447 CmdArgs.push_back("-lc++"); 448 if (Args.hasArg(options::OPT_fexperimental_library)) 449 CmdArgs.push_back("-lc++experimental"); 450 CmdArgs.push_back("-lc++abi"); 451 break; 452 case ToolChain::CST_Libstdcxx: 453 CmdArgs.push_back("-lstdc++"); 454 break; 455 } 456 } 457 458 SanitizerMask WebAssembly::getSupportedSanitizers() const { 459 SanitizerMask Res = ToolChain::getSupportedSanitizers(); 460 if (getTriple().isOSEmscripten()) { 461 Res |= SanitizerKind::Vptr | SanitizerKind::Leak | SanitizerKind::Address; 462 } 463 // -fsanitize=function places two words before the function label, which are 464 // -unsupported. 465 Res &= ~SanitizerKind::Function; 466 return Res; 467 } 468 469 Tool *WebAssembly::buildLinker() const { 470 return new tools::wasm::Linker(*this); 471 } 472 473 void WebAssembly::addLibCxxIncludePaths( 474 const llvm::opt::ArgList &DriverArgs, 475 llvm::opt::ArgStringList &CC1Args) const { 476 const Driver &D = getDriver(); 477 std::string SysRoot = computeSysRoot(); 478 std::string LibPath = SysRoot + "/include"; 479 const std::string MultiarchTriple = 480 getMultiarchTriple(D, getTriple(), SysRoot); 481 bool IsKnownOs = (getTriple().getOS() != llvm::Triple::UnknownOS); 482 483 std::string Version = detectLibcxxVersion(LibPath); 484 if (Version.empty()) 485 return; 486 487 // First add the per-target include path if the OS is known. 488 if (IsKnownOs) { 489 std::string TargetDir = LibPath + "/" + MultiarchTriple + "/c++/" + Version; 490 addSystemInclude(DriverArgs, CC1Args, TargetDir); 491 } 492 493 // Second add the generic one. 494 addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version); 495 } 496 497 void WebAssembly::addLibStdCXXIncludePaths( 498 const llvm::opt::ArgList &DriverArgs, 499 llvm::opt::ArgStringList &CC1Args) const { 500 // We cannot use GCCInstallationDetector here as the sysroot usually does 501 // not contain a full GCC installation. 502 // Instead, we search the given sysroot for /usr/include/xx, similar 503 // to how we do it for libc++. 504 const Driver &D = getDriver(); 505 std::string SysRoot = computeSysRoot(); 506 std::string LibPath = SysRoot + "/include"; 507 const std::string MultiarchTriple = 508 getMultiarchTriple(D, getTriple(), SysRoot); 509 bool IsKnownOs = (getTriple().getOS() != llvm::Triple::UnknownOS); 510 511 // This is similar to detectLibcxxVersion() 512 std::string Version; 513 { 514 std::error_code EC; 515 Generic_GCC::GCCVersion MaxVersion = 516 Generic_GCC::GCCVersion::Parse("0.0.0"); 517 SmallString<128> Path(LibPath); 518 llvm::sys::path::append(Path, "c++"); 519 for (llvm::vfs::directory_iterator LI = getVFS().dir_begin(Path, EC), LE; 520 !EC && LI != LE; LI = LI.increment(EC)) { 521 StringRef VersionText = llvm::sys::path::filename(LI->path()); 522 if (VersionText[0] != 'v') { 523 auto Version = Generic_GCC::GCCVersion::Parse(VersionText); 524 if (Version > MaxVersion) 525 MaxVersion = Version; 526 } 527 } 528 if (MaxVersion.Major > 0) 529 Version = MaxVersion.Text; 530 } 531 532 if (Version.empty()) 533 return; 534 535 // First add the per-target include path if the OS is known. 536 if (IsKnownOs) { 537 std::string TargetDir = LibPath + "/c++/" + Version + "/" + MultiarchTriple; 538 addSystemInclude(DriverArgs, CC1Args, TargetDir); 539 } 540 541 // Second add the generic one. 542 addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version); 543 // Third the backward one. 544 addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version + "/backward"); 545 } 546