diff --git a/CMakeLists.txt b/CMakeLists.txt index 51bcc8cecd3..336bbdbc0a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules") include(FindDCompiler) include(CheckIncludeFile) +include(CheckIncludeFileCXX) include(CheckLibraryExists) include(CheckCXXCompilerFlag) include(CheckDSourceCompiles) @@ -80,7 +81,7 @@ set(CONF_INST_DIR ${SYSCONF_INSTALL_DIR} CACHE PATH "Directory ldc.conf is insta # Note: LIB_SUFFIX should perhaps be renamed to LDC_LIBDIR_SUFFIX. set(LIB_SUFFIX "" CACHE STRING "Appended to the library installation directory. Set to '64' to install libraries into ${PREFIX}/lib64.") -# The following flags are currently not well tested, expect the build to fail. +# The following flag is currently not well tested, expect the build to fail. option(GENERATE_OFFTI "generate complete ClassInfo.offTi arrays") mark_as_advanced(GENERATE_OFFTI) @@ -342,6 +343,8 @@ set(DRV_SRC driver/tool.cpp driver/archiver.cpp driver/linker.cpp + driver/linker-gcc.cpp + driver/linker-msvc.cpp driver/main.cpp ${CMAKE_BINARY_DIR}/driver/ldc-version.cpp ) @@ -426,8 +429,6 @@ include_directories( SYSTEM ) append("-I${PROJECT_SOURCE_DIR}" DDMD_DFLAGS) append("-I${PROJECT_BINARY_DIR}" DDMD_DFLAGS) - - if(MSVC) include_directories(${PROJECT_SOURCE_DIR}/vcbuild) endif() @@ -446,12 +447,31 @@ if(GENERATE_OFFTI) append("-DGENERATE_OFFTI" LDC_CXXFLAGS) endif() -option(RISCV_LLVM_DEV, "full RISC-V support with riscv-llvm") -mark_as_advanced(RISCV_LLVM_DEV) +# +# LLD integration (requires LLVM >= 3.9 with LLD headers & libs) +# +set(LDC_WITH_LLD OFF) +if(LDC_LLVM_VER GREATER 308) + # check for LLD header + unset(LDC_WITH_LLD) + if(NOT MSVC) + set(CMAKE_REQUIRED_FLAGS -std=c++11) + endif() + set(CMAKE_REQUIRED_INCLUDES ${LLVM_INCLUDE_DIRS}) + CHECK_INCLUDE_FILE_CXX(lld/Driver/Driver.h LDC_WITH_LLD) + unset(CMAKE_REQUIRED_FLAGS) + unset(CMAKE_REQUIRED_INCLUDES) + if(LDC_WITH_LLD) + message(STATUS "Building LDC with LLD support") + append("-DLDC_WITH_LLD" LDC_CXXFLAGS) + endif() +endif() # # Enable building with riscv-llvm, for full RISC-V support. # +option(RISCV_LLVM_DEV, "full RISC-V support with riscv-llvm") +mark_as_advanced(RISCV_LLVM_DEV) if(RISCV_LLVM_DEV) append("-DRISCV_LLVM_DEV" LDC_CXXFLAGS) endif() @@ -546,6 +566,13 @@ add_custom_target(${LDMD_EXE} ALL DEPENDS ${LDMD_EXE_FULL}) # Figure out how to link the main LDC executable, for which we need to take the # LLVM flags into account. set(LDC_LINKERFLAG_LIST "${SANITIZE_LDFLAGS};${LLVM_LIBRARIES};${LLVM_LDFLAGS}") +if(LDC_WITH_LLD) + if(MSVC) + list(APPEND LDC_LINKERFLAG_LIST lldCOFF.lib lldCore.lib lldDriver.lib) + else() + set(LDC_LINKERFLAG_LIST "-llldCOFF;-llldCore;-llldDriver;${LDC_LINKERFLAG_LIST}") + endif() +endif() set(LDC_LINK_MANUALLY OFF) if(UNIX AND (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"))) diff --git a/ddmd/mars.d b/ddmd/mars.d index 8bd8a9a2f33..c4e2802f22c 100644 --- a/ddmd/mars.d +++ b/ddmd/mars.d @@ -64,9 +64,10 @@ version(IN_LLVM) // in driver/main.cpp void addDefaultVersionIdentifiers(); void codegenModules(ref Modules modules); + // in driver/archiver.cpp + int createStaticLibrary(); // in driver/linker.cpp int linkObjToBinary(); - int createStaticLibrary(); void deleteExeFile(); int runProgram(); } diff --git a/driver/archiver.cpp b/driver/archiver.cpp index 551e023c484..8a8aacbb21a 100644 --- a/driver/archiver.cpp +++ b/driver/archiver.cpp @@ -6,17 +6,16 @@ // License. See LLVM LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// -// Builds up (relatively) standard unix archive files (.a) containing LLVM -// bitcode or other files. -// -//===----------------------------------------------------------------------===// -#if LDC_LLVM_VER >= 309 +#include "errors.h" +#include "globals.h" +#include "driver/cl_options.h" +#include "driver/tool.h" +#include "gen/logger.h" +#include "llvm/ADT/Triple.h" -#include "driver/archiver.h" +#if LDC_LLVM_VER >= 309 -#include "llvm/ADT/Triple.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/MachO.h" @@ -42,7 +41,7 @@ using namespace llvm; * support for `llvm-ar rcs ...`. * It also makes sure the process isn't simply exited whenever a problem arises. */ -namespace { +namespace llvm_ar { StringRef ArchiveName; std::vector Members; @@ -237,13 +236,13 @@ int performWriteOperation() { return performWriteOperation(nullptr, nullptr); } -} // anonymous namespace +} // namespace llvm_ar //////////////////////////////////////////////////////////////////////////////// -namespace ldc { +namespace { -int ar(ArrayRef args) { +int internalAr(ArrayRef args) { if (args.size() < 4 || strcmp(args[0], "llvm-ar") != 0 || strcmp(args[1], "rcs") != 0) { llvm_unreachable( @@ -252,16 +251,17 @@ int ar(ArrayRef args) { return -1; } - ArchiveName = args[2]; + llvm_ar::ArchiveName = args[2]; auto membersSlice = args.slice(3); - Members.clear(); - Members.insert(Members.end(), membersSlice.begin(), membersSlice.end()); + llvm_ar::Members.clear(); + llvm_ar::Members.insert(llvm_ar::Members.end(), membersSlice.begin(), + membersSlice.end()); - return performWriteOperation(); + return llvm_ar::performWriteOperation(); } -int lib(ArrayRef args) { +int internalLib(ArrayRef args) { if (args.size() < 1 || strcmp(args[0], "llvm-lib.exe") != 0) { llvm_unreachable("Expected archiver command line: llvm-lib.exe ..."); return -1; @@ -270,6 +270,110 @@ int lib(ArrayRef args) { return libDriverMain(args); } -} // namespace ldc +} // anonymous namespace #endif // LDC_LLVM_VER >= 309 + +//////////////////////////////////////////////////////////////////////////////// + +static llvm::cl::opt ar("ar", llvm::cl::desc("Archiver"), + llvm::cl::Hidden, llvm::cl::ZeroOrMore); + +int createStaticLibrary() { + Logger::println("*** Creating static library ***"); + + const bool isTargetMSVC = + global.params.targetTriple->isWindowsMSVCEnvironment(); + +#if LDC_LLVM_VER >= 309 + const bool useInternalArchiver = ar.empty(); +#else + const bool useInternalArchiver = false; +#endif + + // find archiver + std::string tool; + if (useInternalArchiver) { + tool = isTargetMSVC ? "llvm-lib.exe" : "llvm-ar"; + } else { +#ifdef _WIN32 + if (isTargetMSVC) + windows::setupMsvcEnvironment(); +#endif + + tool = getProgram(isTargetMSVC ? "lib.exe" : "ar", &ar); + } + + // build arguments + std::vector args; + + // ask ar to create a new library + if (!isTargetMSVC) { + args.push_back("rcs"); + } + + // ask lib.exe to be quiet + if (isTargetMSVC) { + args.push_back("/NOLOGO"); + } + + // output filename + std::string libName; + if (global.params.libname) { // explicit + // DMD adds the default extension if there is none + libName = opts::invokedByLDMD + ? FileName::defaultExt(global.params.libname, global.lib_ext) + : global.params.libname; + } else { // infer from first object file + libName = global.params.objfiles->dim + ? FileName::removeExt((*global.params.objfiles)[0]) + : "a.out"; + libName += '.'; + libName += global.lib_ext; + } + + // DMD creates static libraries in the objects directory (unless using an + // absolute output path via `-of`). + if (opts::invokedByLDMD && global.params.objdir && + !FileName::absolute(libName.c_str())) { + libName = FileName::combine(global.params.objdir, libName.c_str()); + } + + if (isTargetMSVC) { + args.push_back("/OUT:" + libName); + } else { + args.push_back(libName); + } + + // object files + for (auto objfile : *global.params.objfiles) { + args.push_back(objfile); + } + + // .res/.def files for lib.exe + if (isTargetMSVC) { + if (global.params.resfile) + args.push_back(global.params.resfile); + if (global.params.deffile) + args.push_back(std::string("/DEF:") + global.params.deffile); + } + + // create path to the library + createDirectoryForFileOrFail(libName); + +#if LDC_LLVM_VER >= 309 + if (useInternalArchiver) { + const auto fullArgs = getFullArgs(tool, args, global.params.verbose); + + const int exitCode = + isTargetMSVC ? internalLib(fullArgs) : internalAr(fullArgs); + if (exitCode) + error(Loc(), "%s failed with status: %d", tool.c_str(), exitCode); + + return exitCode; + } +#endif + + // invoke external archiver + return executeToolAndWait(tool, args, global.params.verbose); +} diff --git a/driver/archiver.h b/driver/archiver.h index 32e1477f6a2..64a1b19b85e 100644 --- a/driver/archiver.h +++ b/driver/archiver.h @@ -1,4 +1,4 @@ -//===-- driver/archiver.h - Creating static libs via LLVM--------*- C++ -*-===// +//===-- driver/archiver.h - Creating static libraries -----------*- C++ -*-===// // // LDC – the LLVM D compiler // @@ -6,22 +6,14 @@ // file for details. // //===----------------------------------------------------------------------===// -// -// Provides an interface to LLVM built-in static lib generation via llvm-lib -// (MSVC targets) or llvm-ar (all other targets). -// -//===----------------------------------------------------------------------===// #ifndef LDC_DRIVER_ARCHIVER_H #define LDC_DRIVER_ARCHIVER_H -#if LDC_LLVM_VER >= 309 -#include "llvm/ADT/ArrayRef.h" - -namespace ldc { -int ar(llvm::ArrayRef args); -int lib(llvm::ArrayRef args); -} -#endif // LDC_LLVM_VER >= 309 +/** + * Create a static library from object files. + * @return 0 on success. + */ +int createStaticLibrary(); #endif // !LDC_DRIVER_ARCHIVER_H diff --git a/driver/linker-gcc.cpp b/driver/linker-gcc.cpp new file mode 100644 index 00000000000..0d7a8e33ebf --- /dev/null +++ b/driver/linker-gcc.cpp @@ -0,0 +1,445 @@ +//===-- linker-gcc.cpp ----------------------------------------------------===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// + +#include "errors.h" +#include "driver/cl_options.h" +#include "driver/exe_path.h" +#include "driver/tool.h" +#include "gen/irstate.h" +#include "gen/logger.h" +#include "gen/optimizer.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" + +////////////////////////////////////////////////////////////////////////////// + +static llvm::cl::opt + ltoLibrary("flto-binary", llvm::cl::ZeroOrMore, + llvm::cl::desc("Set the linker LTO plugin library file (e.g. " + "LLVMgold.so (Unixes) or libLTO.dylib (Darwin))"), + llvm::cl::value_desc("file")); + +////////////////////////////////////////////////////////////////////////////// + +namespace { + +class ArgsBuilder { +public: + std::vector args; + + virtual ~ArgsBuilder() = default; + + void build(llvm::StringRef outputPath, + llvm::cl::boolOrDefault fullyStaticFlag); + +private: + virtual void addSanitizers(); + virtual void addUserSwitches(); + void addDefaultLibs(); + virtual void addArch(); + +#if LDC_LLVM_VER >= 309 + void addLTOGoldPluginFlags(); + void addDarwinLTOFlags(); + void addLTOLinkFlags(); +#endif + + virtual void addLdFlag(const llvm::Twine &flag) { + args.push_back(("-Wl," + flag).str()); + } + + virtual void addLdFlag(const llvm::Twine &flag1, const llvm::Twine &flag2) { + args.push_back(("-Wl," + flag1 + "," + flag2).str()); + } +}; + +////////////////////////////////////////////////////////////////////////////// +// LTO functionality + +#if LDC_LLVM_VER >= 309 + +std::string getLTOGoldPluginPath() { + if (!ltoLibrary.empty()) { + if (llvm::sys::fs::exists(ltoLibrary)) + return ltoLibrary; + + error(Loc(), "-flto-binary: file '%s' not found", ltoLibrary.c_str()); + fatal(); + } else { + std::string searchPaths[] = { + // The plugin packaged with LDC has a "-ldc" suffix. + exe_path::prependLibDir("LLVMgold-ldc.so"), + // Perhaps the user copied the plugin to LDC's lib dir. + exe_path::prependLibDir("LLVMgold.so"), +#if __LP64__ + "/usr/local/lib64/LLVMgold.so", +#endif + "/usr/local/lib/LLVMgold.so", +#if __LP64__ + "/usr/lib64/LLVMgold.so", +#endif + "/usr/lib/LLVMgold.so", + "/usr/lib/bfd-plugins/LLVMgold.so", + }; + + // Try all searchPaths and early return upon the first path found. + for (const auto &p : searchPaths) { + if (llvm::sys::fs::exists(p)) + return p; + } + + error(Loc(), "The LLVMgold.so plugin (needed for LTO) was not found. You " + "can specify its path with -flto-binary=."); + fatal(); + } +} + +void ArgsBuilder::addLTOGoldPluginFlags() { + addLdFlag("-plugin", getLTOGoldPluginPath()); + + if (opts::isUsingThinLTO()) + addLdFlag("-plugin-opt=thinlto"); + + if (!opts::mCPU.empty()) + addLdFlag(llvm::Twine("-plugin-opt=mcpu=") + opts::mCPU); + + // Use the O-level passed to LDC as the O-level for LTO, but restrict it to + // the [0, 3] range that can be passed to the linker plugin. + static char optChars[15] = "-plugin-opt=O0"; + optChars[13] = '0' + std::min(optLevel(), 3); + addLdFlag(optChars); + +#if LDC_LLVM_VER >= 400 + const llvm::TargetOptions &TO = gTargetMachine->Options; + if (TO.FunctionSections) + addLdFlag("-plugin-opt=-function-sections"); + if (TO.DataSections) + addLdFlag("-plugin-opt=-data-sections"); +#endif +} + +// Returns an empty string when libLTO.dylib was not specified nor found. +std::string getLTOdylibPath() { + if (!ltoLibrary.empty()) { + if (llvm::sys::fs::exists(ltoLibrary)) + return ltoLibrary; + + error(Loc(), "-flto-binary: '%s' not found", ltoLibrary.c_str()); + fatal(); + } else { + // The plugin packaged with LDC has a "-ldc" suffix. + std::string searchPath = exe_path::prependLibDir("libLTO-ldc.dylib"); + if (llvm::sys::fs::exists(searchPath)) + return searchPath; + + return ""; + } +} + +void ArgsBuilder::addDarwinLTOFlags() { + std::string dylibPath = getLTOdylibPath(); + if (!dylibPath.empty()) { + args.push_back("-lto_library"); + args.push_back(std::move(dylibPath)); + } +} + +/// Adds the required linker flags for LTO builds to args. +void ArgsBuilder::addLTOLinkFlags() { + if (global.params.targetTriple->isOSLinux() || + global.params.targetTriple->isOSFreeBSD() || + global.params.targetTriple->isOSNetBSD() || + global.params.targetTriple->isOSOpenBSD() || + global.params.targetTriple->isOSDragonFly()) { + // Assume that ld.gold or ld.bfd is used with plugin support. + addLTOGoldPluginFlags(); + } else if (global.params.targetTriple->isOSDarwin()) { + addDarwinLTOFlags(); + } +} + +#endif // LDC_LLVM_VER >= 309 + +////////////////////////////////////////////////////////////////////////////// + +void ArgsBuilder::build(llvm::StringRef outputPath, + llvm::cl::boolOrDefault fullyStaticFlag) { + // object files + for (auto objfile : *global.params.objfiles) { + args.push_back(objfile); + } + + // Link with profile-rt library when generating an instrumented binary. + // profile-rt uses Phobos (MD5 hashing) and therefore must be passed on the + // commandline before Phobos. + if (global.params.genInstrProf) { +#if LDC_LLVM_VER >= 308 + if (global.params.targetTriple->isOSLinux()) { + // For Linux, explicitly define __llvm_profile_runtime as undefined + // symbol, so that the initialization part of profile-rt is linked in. + addLdFlag("-u", llvm::getInstrProfRuntimeHookVarName()); + } +#endif + args.push_back("-lldc-profile-rt"); + } + + // user libs + for (auto libfile : *global.params.libfiles) { + args.push_back(libfile); + } + + if (global.params.dll) { + args.push_back("-shared"); + } + + if (fullyStaticFlag == llvm::cl::BOU_TRUE) { + args.push_back("-static"); + } + + args.push_back("-o"); + args.push_back(outputPath); + + addSanitizers(); + +#if LDC_LLVM_VER >= 309 + // Add LTO link flags before adding the user link switches, such that the user + // can pass additional options to the LTO plugin. + if (opts::isUsingLTO()) + addLTOLinkFlags(); +#endif + + addUserSwitches(); + + // libs added via pragma(lib, libname) + for (auto ls : *global.params.linkswitches) { + args.push_back(ls); + } + + if (global.params.targetTriple->getOS() == llvm::Triple::Linux) { + // Make sure we don't do --gc-sections when generating a profile- + // instrumented binary. The runtime relies on magic sections, which + // would be stripped by gc-section on older version of ld, see bug: + // https://sourceware.org/bugzilla/show_bug.cgi?id=19161 + if (!opts::disableLinkerStripDead && !global.params.genInstrProf) { + addLdFlag("--gc-sections"); + } + } + + addDefaultLibs(); + + addArch(); +} + +////////////////////////////////////////////////////////////////////////////// + +// Requires clang. +void ArgsBuilder::addSanitizers() { + if (opts::sanitize == opts::AddressSanitizer) { + args.push_back("-fsanitize=address"); + } + if (opts::sanitize == opts::MemorySanitizer) { + args.push_back("-fsanitize=memory"); + } + if (opts::sanitize == opts::ThreadSanitizer) { + args.push_back("-fsanitize=thread"); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void ArgsBuilder::addUserSwitches() { + // additional linker and cc switches (preserve order across both lists) + for (unsigned ilink = 0, icc = 0;;) { + unsigned linkpos = ilink < opts::linkerSwitches.size() + ? opts::linkerSwitches.getPosition(ilink) + : std::numeric_limits::max(); + unsigned ccpos = icc < opts::ccSwitches.size() + ? opts::ccSwitches.getPosition(icc) + : std::numeric_limits::max(); + if (linkpos < ccpos) { + const std::string &p = opts::linkerSwitches[ilink++]; + // Don't push -l and -L switches using -Xlinker, but pass them indirectly + // via GCC. This makes sure user-defined paths take precedence over + // GCC's builtin LIBRARY_PATHs. + // Options starting with `-Wl,`, -shared or -static are not handled by + // the linker and must be passed to the driver. + auto str = llvm::StringRef(p); + if (!(str.startswith("-l") || str.startswith("-L") || + str.startswith("-Wl,") || str.startswith("-shared") || + str.startswith("-static"))) { + args.push_back("-Xlinker"); + } + args.push_back(p); + } else if (ccpos < linkpos) { + args.push_back(opts::ccSwitches[icc++]); + } else { + break; + } + } +} + +////////////////////////////////////////////////////////////////////////////// + +void ArgsBuilder::addDefaultLibs() { + bool addSoname = false; + + switch (global.params.targetTriple->getOS()) { + case llvm::Triple::Linux: + addSoname = true; + if (global.params.targetTriple->getEnvironment() == llvm::Triple::Android) { + args.push_back("-ldl"); + args.push_back("-lm"); + break; + } + args.push_back("-lrt"); + // fallthrough + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + args.push_back("-ldl"); + // fallthrough + case llvm::Triple::FreeBSD: + case llvm::Triple::NetBSD: + case llvm::Triple::OpenBSD: + case llvm::Triple::DragonFly: + addSoname = true; + args.push_back("-lpthread"); + args.push_back("-lm"); + break; + + case llvm::Triple::Solaris: + args.push_back("-lm"); + args.push_back("-lumem"); + args.push_back("-lsocket"); + args.push_back("-lnsl"); + break; + + default: + // OS not yet handled, will probably lead to linker errors. + // FIXME: Win32. + break; + } + + if (global.params.targetTriple->isWindowsGNUEnvironment()) { + // This is really more of a kludge, as linking in the Winsock functions + // should be handled by the pragma(lib, ...) in std.socket, but it + // makes LDC behave as expected for now. + args.push_back("-lws2_32"); + } + + if (global.params.dll && addSoname && !opts::soname.empty()) { + addLdFlag("-soname", opts::soname); + } +} + +////////////////////////////////////////////////////////////////////////////// + +void ArgsBuilder::addArch() { + // Only specify -m32/-m64 for architectures where the two variants actually + // exist (as e.g. the GCC ARM toolchain doesn't recognize the switches). + // MIPS does not have -m32/-m64 but requires -mabi=. + if (global.params.targetTriple->get64BitArchVariant().getArch() != + llvm::Triple::UnknownArch && + global.params.targetTriple->get32BitArchVariant().getArch() != + llvm::Triple::UnknownArch) { + if (global.params.targetTriple->get64BitArchVariant().getArch() == + llvm::Triple::mips64 || + global.params.targetTriple->get64BitArchVariant().getArch() == + llvm::Triple::mips64el) { + switch (getMipsABI()) { + case MipsABI::EABI: + args.push_back("-mabi=eabi"); + break; + case MipsABI::O32: + args.push_back("-mabi=32"); + break; + case MipsABI::N32: + args.push_back("-mabi=n32"); + break; + case MipsABI::N64: + args.push_back("-mabi=64"); + break; + case MipsABI::Unknown: + break; + } + } else { + switch (global.params.targetTriple->getArch()) { + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_be: +#if LDC_LLVM_VER == 305 + case llvm::Triple::arm64: + case llvm::Triple::arm64_be: +#endif + break; + default: + if (global.params.is64bit) { + args.push_back("-m64"); + } else { + args.push_back("-m32"); + } + } + } + } +} + +////////////////////////////////////////////////////////////////////////////// +// (Yet unused) specialization for plain ld. + +class LdArgsBuilder : public ArgsBuilder { + void addSanitizers() override {} + + void addUserSwitches() override { + if (!opts::ccSwitches.empty()) { + warning(Loc(), "Ignoring -Xcc options"); + } + + args.insert(args.end(), opts::linkerSwitches.begin(), + opts::linkerSwitches.end()); + } + + void addArch() override {} + + void addLdFlag(const llvm::Twine &flag) override { + args.push_back(flag.str()); + } + + void addLdFlag(const llvm::Twine &flag1, const llvm::Twine &flag2) override { + args.push_back(flag1.str()); + args.push_back(flag2.str()); + } +}; + +} // anonymous namespace + +////////////////////////////////////////////////////////////////////////////// + +int linkObjToBinaryGcc(llvm::StringRef outputPath, bool useInternalLinker, + llvm::cl::boolOrDefault fullyStaticFlag) { + // find gcc for linking + const std::string tool = getGcc(); + + // build arguments + ArgsBuilder argsBuilder; + argsBuilder.build(outputPath, fullyStaticFlag); + + Logger::println("Linking with: "); + Stream logstr = Logger::cout(); + for (const auto &arg : argsBuilder.args) { + if (!arg.empty()) { + logstr << "'" << arg << "' "; + } + } + logstr << "\n"; // FIXME where's flush ? + + // try to call linker + return executeToolAndWait(tool, argsBuilder.args, global.params.verbose); +} diff --git a/driver/linker-msvc.cpp b/driver/linker-msvc.cpp new file mode 100644 index 00000000000..6ead29f5e2c --- /dev/null +++ b/driver/linker-msvc.cpp @@ -0,0 +1,184 @@ +//===-- linker-msvc.cpp ---------------------------------------------------===// +// +// LDC – the LLVM D compiler +// +// This file is distributed under the BSD-style LDC license. See the LICENSE +// file for details. +// +//===----------------------------------------------------------------------===// + +#include "errors.h" +#include "driver/cl_options.h" +#include "driver/tool.h" +#include "gen/logger.h" + +#if LDC_WITH_LLD +#include "lld/Driver/Driver.h" +#endif + +////////////////////////////////////////////////////////////////////////////// + +static llvm::cl::opt mscrtlib( + "mscrtlib", llvm::cl::ZeroOrMore, llvm::cl::value_desc("name"), + llvm::cl::desc( + "MS C runtime library to link against (libcmt[d] / msvcrt[d])")); + +////////////////////////////////////////////////////////////////////////////// + +namespace { + +void addMscrtLibs(std::vector &args, + llvm::cl::boolOrDefault fullyStaticFlag) { + llvm::StringRef mscrtlibName = mscrtlib; + if (mscrtlibName.empty()) { + // default to static release variant + mscrtlibName = fullyStaticFlag != llvm::cl::BOU_FALSE ? "libcmt" : "msvcrt"; + } + + args.push_back(("/DEFAULTLIB:" + mscrtlibName).str()); + + const bool isStatic = mscrtlibName.startswith_lower("libcmt"); + const bool isDebug = + mscrtlibName.endswith_lower("d") || mscrtlibName.endswith_lower("d.lib"); + + const llvm::StringRef prefix = isStatic ? "lib" : ""; + const llvm::StringRef suffix = isDebug ? "d" : ""; + + args.push_back(("/DEFAULTLIB:" + prefix + "vcruntime" + suffix).str()); +} + +} // anonymous namespace + +////////////////////////////////////////////////////////////////////////////// + +int linkObjToBinaryMSVC(llvm::StringRef outputPath, bool useInternalLinker, + llvm::cl::boolOrDefault fullyStaticFlag) { + if (!opts::ccSwitches.empty()) { + error(Loc(), "-Xcc is not supported for MSVC"); + fatal(); + } + +#ifdef _WIN32 + windows::setupMsvcEnvironment(); +#endif + + const std::string tool = "link.exe"; + + // build arguments + std::vector args; + + args.push_back("/NOLOGO"); + + // specify that the image will contain a table of safe exception handlers + // and can handle addresses >2GB (32bit only) + if (!global.params.is64bit) { + args.push_back("/SAFESEH"); + args.push_back("/LARGEADDRESSAWARE"); + } + + // output debug information + if (global.params.symdebug) { + args.push_back("/DEBUG"); + } + + // remove dead code and fold identical COMDATs + if (opts::disableLinkerStripDead) { + args.push_back("/OPT:NOREF"); + } else { + args.push_back("/OPT:REF"); + args.push_back("/OPT:ICF"); + } + + // add C runtime libs + addMscrtLibs(args, fullyStaticFlag); + + // specify creation of DLL + if (global.params.dll) { + args.push_back("/DLL"); + } + + args.push_back(("/OUT:" + outputPath).str()); + + // object files + for (auto objfile : *global.params.objfiles) { + args.push_back(objfile); + } + + // .res/.def files + if (global.params.resfile) + args.push_back(global.params.resfile); + if (global.params.deffile) + args.push_back(std::string("/DEF:") + global.params.deffile); + + // Link with profile-rt library when generating an instrumented binary + // profile-rt depends on Phobos (MD5 hashing). + if (global.params.genInstrProf) { + args.push_back("ldc-profile-rt.lib"); + // profile-rt depends on ws2_32 for symbol `gethostname` + args.push_back("ws2_32.lib"); + } + + // user libs + for (auto libfile : *global.params.libfiles) { + args.push_back(libfile); + } + + // additional linker switches + auto addSwitch = [&](std::string str) { + if (str.length() > 2) { + // rewrite common -L and -l switches + if (str[0] == '-' && str[1] == 'L') { + str = "/LIBPATH:" + str.substr(2); + } else if (str[0] == '-' && str[1] == 'l') { + str = str.substr(2) + ".lib"; + } + } + args.push_back(str); + }; + + for (const auto &str : opts::linkerSwitches) { + addSwitch(str); + } + + for (auto ls : *global.params.linkswitches) { + addSwitch(ls); + } + + // default libs + // TODO check which libaries are necessary + args.push_back("kernel32.lib"); + args.push_back("user32.lib"); + args.push_back("gdi32.lib"); + args.push_back("winspool.lib"); + args.push_back("shell32.lib"); // required for dmain2.d + args.push_back("ole32.lib"); + args.push_back("oleaut32.lib"); + args.push_back("uuid.lib"); + args.push_back("comdlg32.lib"); + args.push_back("advapi32.lib"); + + Logger::println("Linking with: "); + Stream logstr = Logger::cout(); + for (const auto &arg : args) { + if (!arg.empty()) { + logstr << "'" << arg << "' "; + } + } + logstr << "\n"; // FIXME where's flush ? + +#if LDC_WITH_LLD + if (useInternalLinker) { + const auto fullArgs = + getFullArgs("lld-link.exe", args, global.params.verbose); + + const bool success = lld::coff::link(fullArgs); + if (!success) + error(Loc(), "linking with LLD failed"); + + return success ? 0 : 1; + } +#endif + + // try to call linker + return executeToolAndWait(tool, args, global.params.verbose); +} diff --git a/driver/linker.cpp b/driver/linker.cpp index 57215dbba04..4a5caa87602 100644 --- a/driver/linker.cpp +++ b/driver/linker.cpp @@ -7,68 +7,47 @@ // //===----------------------------------------------------------------------===// -#include "driver/linker.h" -#include "mars.h" -#include "module.h" -#include "root.h" -#include "driver/archiver.h" +#include "errors.h" #include "driver/cl_options.h" -#include "driver/exe_path.h" +#include "driver/linker.h" #include "driver/tool.h" -#include "gen/irstate.h" #include "gen/llvm.h" #include "gen/logger.h" -#include "gen/optimizer.h" -#include "llvm/ADT/Triple.h" #include "llvm/IRReader/IRReader.h" #include "llvm/Linker/Linker.h" -#include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" #include "llvm/Support/SourceMgr.h" -#include "llvm/Target/TargetMachine.h" -#include "llvm/Target/TargetOptions.h" - -#include ////////////////////////////////////////////////////////////////////////////// -static llvm::cl::opt +static llvm::cl::opt staticFlag("static", llvm::cl::ZeroOrMore, llvm::cl::desc("Create a statically linked binary, including " "all system dependencies")); -static llvm::cl::opt mscrtlib( - "mscrtlib", llvm::cl::ZeroOrMore, llvm::cl::value_desc("name"), - llvm::cl::desc( - "MS C runtime library to link against (libcmt[d] / msvcrt[d])")); - -static llvm::cl::opt - ltoLibrary("flto-binary", llvm::cl::ZeroOrMore, - llvm::cl::desc("Set the linker LTO plugin library file (e.g. " - "LLVMgold.so (Unixes) or libLTO.dylib (Darwin))"), - llvm::cl::value_desc("file")); - -static llvm::cl::opt ar("ar", llvm::cl::desc("Archiver"), - llvm::cl::Hidden, llvm::cl::ZeroOrMore); +#if LDC_WITH_LLD +static llvm::cl::opt + useInternalLinker("link-internally", llvm::cl::ZeroOrMore, llvm::cl::Hidden, + llvm::cl::desc("Use internal LLD for linking")); +#else +constexpr bool useInternalLinker = false; +#endif ////////////////////////////////////////////////////////////////////////////// -static void CreateDirectoryOnDisk(llvm::StringRef fileName) { - auto dir = llvm::sys::path::parent_path(fileName); - if (!dir.empty() && !llvm::sys::fs::exists(dir)) { - if (auto ec = llvm::sys::fs::create_directories(dir)) { - error(Loc(), "failed to create path to file: %s\n%s", dir.data(), - ec.message().c_str()); - fatal(); - } - } -} +// linker-gcc.cpp +int linkObjToBinaryGcc(llvm::StringRef outputPath, bool useInternalLinker, + llvm::cl::boolOrDefault fullyStaticFlag); + +// linker-msvc.cpp +int linkObjToBinaryMSVC(llvm::StringRef outputPath, bool useInternalLinker, + llvm::cl::boolOrDefault fullyStaticFlag); ////////////////////////////////////////////////////////////////////////////// -static std::string getOutputName(bool const sharedLib) { +static std::string getOutputName() { const auto &triple = *global.params.targetTriple; + const bool sharedLib = global.params.dll; const char *extension = nullptr; if (sharedLib) { @@ -111,129 +90,11 @@ static std::string getOutputName(bool const sharedLib) { } ////////////////////////////////////////////////////////////////////////////// -// LTO functionality - -#if LDC_LLVM_VER >= 309 - -namespace { - -void addLinkerFlag(std::vector &args, const llvm::Twine &flag) { - args.push_back("-Xlinker"); - args.push_back(flag.str()); -} - -std::string getLTOGoldPluginPath() { - if (!ltoLibrary.empty()) { - if (llvm::sys::fs::exists(ltoLibrary)) - return ltoLibrary; - - error(Loc(), "-flto-binary: file '%s' not found", ltoLibrary.c_str()); - fatal(); - } else { - std::string searchPaths[] = { - // The plugin packaged with LDC has a "-ldc" suffix. - exe_path::prependLibDir("LLVMgold-ldc.so"), - // Perhaps the user copied the plugin to LDC's lib dir. - exe_path::prependLibDir("LLVMgold.so"), -#if __LP64__ - "/usr/local/lib64/LLVMgold.so", -#endif - "/usr/local/lib/LLVMgold.so", -#if __LP64__ - "/usr/lib64/LLVMgold.so", -#endif - "/usr/lib/LLVMgold.so", - "/usr/lib/bfd-plugins/LLVMgold.so", - }; - - // Try all searchPaths and early return upon the first path found. - for (const auto &p : searchPaths) { - if (llvm::sys::fs::exists(p)) - return p; - } - - error(Loc(), "The LLVMgold.so plugin (needed for LTO) was not found. You " - "can specify its path with -flto-binary=."); - fatal(); - } -} - -void addLTOGoldPluginFlags(std::vector &args) { - addLinkerFlag(args, "-plugin"); - addLinkerFlag(args, getLTOGoldPluginPath()); - - if (opts::isUsingThinLTO()) - addLinkerFlag(args, "-plugin-opt=thinlto"); - - if (!opts::mCPU.empty()) - addLinkerFlag(args, llvm::Twine("-plugin-opt=mcpu=") + opts::mCPU); - - // Use the O-level passed to LDC as the O-level for LTO, but restrict it to - // the [0, 3] range that can be passed to the linker plugin. - static char optChars[15] = "-plugin-opt=O0"; - optChars[13] = '0' + std::min(optLevel(), 3); - addLinkerFlag(args, optChars); - -#if LDC_LLVM_VER >= 400 - const llvm::TargetOptions &TO = gTargetMachine->Options; - if (TO.FunctionSections) - addLinkerFlag(args, "-plugin-opt=-function-sections"); - if (TO.DataSections) - addLinkerFlag(args, "-plugin-opt=-data-sections"); -#endif -} - -// Returns an empty string when libLTO.dylib was not specified nor found. -std::string getLTOdylibPath() { - if (!ltoLibrary.empty()) { - if (llvm::sys::fs::exists(ltoLibrary)) - return ltoLibrary; - - error(Loc(), "-flto-binary: '%s' not found", ltoLibrary.c_str()); - fatal(); - } else { - // The plugin packaged with LDC has a "-ldc" suffix. - std::string searchPath = exe_path::prependLibDir("libLTO-ldc.dylib"); - if (llvm::sys::fs::exists(searchPath)) - return searchPath; - - return ""; - } -} - -void addDarwinLTOFlags(std::vector &args) { - std::string dylibPath = getLTOdylibPath(); - if (!dylibPath.empty()) { - args.push_back("-lto_library"); - args.push_back(std::move(dylibPath)); - } -} - -/// Adds the required linker flags for LTO builds to args. -void addLTOLinkFlags(std::vector &args) { - if (global.params.targetTriple->isOSLinux() || - global.params.targetTriple->isOSFreeBSD() || - global.params.targetTriple->isOSNetBSD() || - global.params.targetTriple->isOSOpenBSD() || - global.params.targetTriple->isOSDragonFly()) { - // Assume that ld.gold or ld.bfd is used with plugin support. - addLTOGoldPluginFlags(args); - } else if (global.params.targetTriple->isOSDarwin()) { - addDarwinLTOFlags(args); - } -} -} // anonymous namespace - -#endif // LDC_LLVM_VER >= 309 - -////////////////////////////////////////////////////////////////////////////// - -namespace { #if LDC_LLVM_VER >= 306 /// Insert an LLVM bitcode file into the module -void insertBitcodeIntoModule(const char *bcFile, llvm::Module &M, - llvm::LLVMContext &Context) { +static void insertBitcodeIntoModule(const char *bcFile, llvm::Module &M, + llvm::LLVMContext &Context) { Logger::println("*** Linking-in bitcode file %s ***", bcFile); llvm::SMDiagnostic Err; @@ -249,8 +110,7 @@ void insertBitcodeIntoModule(const char *bcFile, llvm::Module &M, llvm::Linker(&M).linkInModule(loadedModule.release()); #endif } -#endif -} +#endif // LDC_LLVM_VER >= 306 /// Insert LLVM bitcode files into the module void insertBitcodeFiles(llvm::Module &M, llvm::LLVMContext &Ctx, @@ -270,500 +130,24 @@ void insertBitcodeFiles(llvm::Module &M, llvm::LLVMContext &Ctx, ////////////////////////////////////////////////////////////////////////////// -static void appendObjectFiles(std::vector &args) { - for (unsigned i = 0; i < global.params.objfiles->dim; i++) - args.push_back((*global.params.objfiles)[i]); - - if (global.params.targetTriple->isWindowsMSVCEnvironment()) { - if (global.params.resfile) - args.push_back(global.params.resfile); - if (global.params.deffile) - args.push_back(std::string("/DEF:") + global.params.deffile); - } -} - -////////////////////////////////////////////////////////////////////////////// - +// path to the produced executable/shared library static std::string gExePath; -static int linkObjToBinaryGcc(bool sharedLib) { - Logger::println("*** Linking executable ***"); - - // find gcc for linking - const std::string tool = getGcc(); - - // build arguments - std::vector args; - - appendObjectFiles(args); - - // Link with profile-rt library when generating an instrumented binary. - // profile-rt uses Phobos (MD5 hashing) and therefore must be passed on the - // commandline before Phobos. - if (global.params.genInstrProf) { -#if LDC_LLVM_VER >= 308 - if (global.params.targetTriple->isOSLinux()) { - // For Linux, explicitly define __llvm_profile_runtime as undefined - // symbol, so that the initialization part of profile-rt is linked in. - args.push_back( - ("-Wl,-u," + llvm::getInstrProfRuntimeHookVarName()).str()); - } -#endif - args.push_back("-lldc-profile-rt"); - } - - // user libs - for (unsigned i = 0; i < global.params.libfiles->dim; i++) - args.push_back((*global.params.libfiles)[i]); - - // output filename - std::string output = getOutputName(sharedLib); - - if (sharedLib) { - args.push_back("-shared"); - } - - if (staticFlag) { - args.push_back("-static"); - } - - args.push_back("-o"); - args.push_back(output); - - // set the global gExePath - gExePath = output; - // assert(gExePath.isValid()); - - // create path to exe - CreateDirectoryOnDisk(gExePath); - - // Pass sanitizer arguments to linker. Requires clang. - if (opts::sanitize == opts::AddressSanitizer) { - args.push_back("-fsanitize=address"); - } - - if (opts::sanitize == opts::MemorySanitizer) { - args.push_back("-fsanitize=memory"); - } - - if (opts::sanitize == opts::ThreadSanitizer) { - args.push_back("-fsanitize=thread"); - } - -#if LDC_LLVM_VER >= 309 - // Add LTO link flags before adding the user link switches, such that the user - // can pass additional options to the LTO plugin. - if (opts::isUsingLTO()) - addLTOLinkFlags(args); -#endif - - // additional linker and cc switches (preserve order across both lists) - for (unsigned ilink = 0, icc = 0;;) { - unsigned linkpos = ilink < opts::linkerSwitches.size() - ? opts::linkerSwitches.getPosition(ilink) - : std::numeric_limits::max(); - unsigned ccpos = icc < opts::ccSwitches.size() - ? opts::ccSwitches.getPosition(icc) - : std::numeric_limits::max(); - if (linkpos < ccpos) { - const std::string &p = opts::linkerSwitches[ilink++]; - // Don't push -l and -L switches using -Xlinker, but pass them indirectly - // via GCC. This makes sure user-defined paths take precedence over - // GCC's builtin LIBRARY_PATHs. - // Options starting with `-Wl,`, -shared or -static are not handled by - // the linker and must be passed to the driver. - auto str = llvm::StringRef(p); - if (!(str.startswith("-l") || str.startswith("-L") || - str.startswith("-Wl,") || str.startswith("-shared") || - str.startswith("-static"))) { - args.push_back("-Xlinker"); - } - args.push_back(p); - } else if (ccpos < linkpos) { - args.push_back(opts::ccSwitches[icc++]); - } else { - break; - } - } - - // libs added via pragma(lib, libname) - for (unsigned i = 0; i < global.params.linkswitches->dim; i++) { - args.push_back((*global.params.linkswitches)[i]); - } - - // default libs - bool addSoname = false; - switch (global.params.targetTriple->getOS()) { - case llvm::Triple::Linux: - addSoname = true; - // Make sure we don't do --gc-sections when generating a profile- - // instrumented binary. The runtime relies on magic sections, which - // would be stripped by gc-section on older version of ld, see bug: - // https://sourceware.org/bugzilla/show_bug.cgi?id=19161 - if (!opts::disableLinkerStripDead && !global.params.genInstrProf) { - args.push_back("-Wl,--gc-sections"); - } - if (global.params.targetTriple->getEnvironment() == llvm::Triple::Android) { - args.push_back("-ldl"); - args.push_back("-lm"); - break; - } - args.push_back("-lrt"); - // fallthrough - case llvm::Triple::Darwin: - case llvm::Triple::MacOSX: - args.push_back("-ldl"); - // fallthrough - case llvm::Triple::FreeBSD: - case llvm::Triple::NetBSD: - case llvm::Triple::OpenBSD: - case llvm::Triple::DragonFly: - addSoname = true; - args.push_back("-lpthread"); - args.push_back("-lm"); - break; - - case llvm::Triple::Solaris: - args.push_back("-lm"); - args.push_back("-lumem"); - args.push_back("-lsocket"); - args.push_back("-lnsl"); - break; - - default: - // OS not yet handled, will probably lead to linker errors. - // FIXME: Win32. - break; - } - - if (global.params.targetTriple->isWindowsGNUEnvironment()) { - // This is really more of a kludge, as linking in the Winsock functions - // should be handled by the pragma(lib, ...) in std.socket, but it - // makes LDC behave as expected for now. - args.push_back("-lws2_32"); - } - - // Only specify -m32/-m64 for architectures where the two variants actually - // exist (as e.g. the GCC ARM toolchain doesn't recognize the switches). - // MIPS does not have -m32/-m64 but requires -mabi=. - if (global.params.targetTriple->get64BitArchVariant().getArch() != - llvm::Triple::UnknownArch && - global.params.targetTriple->get32BitArchVariant().getArch() != - llvm::Triple::UnknownArch) { - if (global.params.targetTriple->get64BitArchVariant().getArch() == - llvm::Triple::mips64 || - global.params.targetTriple->get64BitArchVariant().getArch() == - llvm::Triple::mips64el) { - switch (getMipsABI()) { - case MipsABI::EABI: - args.push_back("-mabi=eabi"); - break; - case MipsABI::O32: - args.push_back("-mabi=32"); - break; - case MipsABI::N32: - args.push_back("-mabi=n32"); - break; - case MipsABI::N64: - args.push_back("-mabi=64"); - break; - case MipsABI::Unknown: - break; - } - } else { - switch (global.params.targetTriple->getArch()) { - case llvm::Triple::arm: - case llvm::Triple::armeb: - case llvm::Triple::aarch64: - case llvm::Triple::aarch64_be: -#if LDC_LLVM_VER == 305 - case llvm::Triple::arm64: - case llvm::Triple::arm64_be: -#endif - break; - default: - if (global.params.is64bit) { - args.push_back("-m64"); - } else { - args.push_back("-m32"); - } - } - } - } - - if (global.params.dll && addSoname) { - std::string soname = opts::soname; - if (!soname.empty()) { - args.push_back("-Wl,-soname," + soname); - } - } - - Logger::println("Linking with: "); - Stream logstr = Logger::cout(); - for (const auto &arg : args) { - if (!arg.empty()) { - logstr << "'" << arg << "'" - << " "; - } - } - logstr << "\n"; // FIXME where's flush ? - - // try to call linker - return executeToolAndWait(tool, args, global.params.verbose); -} - ////////////////////////////////////////////////////////////////////////////// -static void addMscrtLibs(std::vector &args) { - llvm::StringRef mscrtlibName = mscrtlib; - if (mscrtlibName.empty()) { - // default to static release variant - mscrtlibName = - staticFlag || staticFlag.getNumOccurrences() == 0 ? "libcmt" : "msvcrt"; - } - - args.push_back(("/DEFAULTLIB:" + mscrtlibName).str()); - - const bool isStatic = mscrtlibName.startswith_lower("libcmt"); - const bool isDebug = - mscrtlibName.endswith_lower("d") || mscrtlibName.endswith_lower("d.lib"); - - const llvm::StringRef prefix = isStatic ? "lib" : ""; - const llvm::StringRef suffix = isDebug ? "d" : ""; - - args.push_back(("/DEFAULTLIB:" + prefix + "vcruntime" + suffix).str()); -} - -static int linkObjToBinaryMSVC(bool sharedLib) { +int linkObjToBinary() { Logger::println("*** Linking executable ***"); - if (!opts::ccSwitches.empty()) { - error(Loc(), "-Xcc is not supported for MSVC"); - fatal(); - } - -#ifdef _WIN32 - windows::setupMsvcEnvironment(); -#endif - - const std::string tool = "link.exe"; - - // build arguments - std::vector args; - - args.push_back("/NOLOGO"); - - // specify that the image will contain a table of safe exception handlers - // and can handle addresses >2GB (32bit only) - if (!global.params.is64bit) { - args.push_back("/SAFESEH"); - args.push_back("/LARGEADDRESSAWARE"); - } - - // output debug information - if (global.params.symdebug) { - args.push_back("/DEBUG"); - } - - // remove dead code and fold identical COMDATs - if (opts::disableLinkerStripDead) { - args.push_back("/OPT:NOREF"); - } else { - args.push_back("/OPT:REF"); - args.push_back("/OPT:ICF"); - } - - // add C runtime libs - addMscrtLibs(args); - - // specify creation of DLL - if (sharedLib) { - args.push_back("/DLL"); - } - - // output filename - std::string output = getOutputName(sharedLib); - - args.push_back("/OUT:" + output); - - appendObjectFiles(args); - - // Link with profile-rt library when generating an instrumented binary - // profile-rt depends on Phobos (MD5 hashing). - if (global.params.genInstrProf) { - args.push_back("ldc-profile-rt.lib"); - // profile-rt depends on ws2_32 for symbol `gethostname` - args.push_back("ws2_32.lib"); - } - - // user libs - for (unsigned i = 0; i < global.params.libfiles->dim; i++) - args.push_back((*global.params.libfiles)[i]); - - // set the global gExePath - gExePath = output; - // assert(gExePath.isValid()); - - // create path to exe - CreateDirectoryOnDisk(gExePath); - - // additional linker switches - auto addSwitch = [&](std::string str) { - if (str.length() > 2) { - // rewrite common -L and -l switches - if (str[0] == '-' && str[1] == 'L') { - str = "/LIBPATH:" + str.substr(2); - } else if (str[0] == '-' && str[1] == 'l') { - str = str.substr(2) + ".lib"; - } - } - args.push_back(str); - }; - - for (const auto &str : opts::linkerSwitches) { - addSwitch(str); - } - - for (unsigned i = 0; i < global.params.linkswitches->dim; i++) { - addSwitch(global.params.linkswitches->data[i]); - } + // remember output path for later + gExePath = getOutputName(); - // default libs - // TODO check which libaries are necessary - args.push_back("kernel32.lib"); - args.push_back("user32.lib"); - args.push_back("gdi32.lib"); - args.push_back("winspool.lib"); - args.push_back("shell32.lib"); // required for dmain2.d - args.push_back("ole32.lib"); - args.push_back("oleaut32.lib"); - args.push_back("uuid.lib"); - args.push_back("comdlg32.lib"); - args.push_back("advapi32.lib"); - - Logger::println("Linking with: "); - Stream logstr = Logger::cout(); - for (const auto &arg : args) { - if (!arg.empty()) { - logstr << "'" << arg << "'" - << " "; - } - } - logstr << "\n"; // FIXME where's flush ? + createDirectoryForFileOrFail(gExePath); - // try to call linker - return executeToolAndWait(tool, args, global.params.verbose); -} - -////////////////////////////////////////////////////////////////////////////// - -int linkObjToBinary() { if (global.params.targetTriple->isWindowsMSVCEnvironment()) { - return linkObjToBinaryMSVC(global.params.dll); + return linkObjToBinaryMSVC(gExePath, useInternalLinker, staticFlag); } - return linkObjToBinaryGcc(global.params.dll); -} - -////////////////////////////////////////////////////////////////////////////// - -int createStaticLibrary() { - Logger::println("*** Creating static library ***"); - - const bool isTargetMSVC = - global.params.targetTriple->isWindowsMSVCEnvironment(); - -#if LDC_LLVM_VER >= 309 - const bool useInternalArchiver = ar.empty(); -#else - const bool useInternalArchiver = false; -#endif - - // find archiver - std::string tool; - if (useInternalArchiver) { - tool = isTargetMSVC ? "llvm-lib.exe" : "llvm-ar"; - } else { -#ifdef _WIN32 - if (isTargetMSVC) - windows::setupMsvcEnvironment(); -#endif - - tool = getProgram(isTargetMSVC ? "lib.exe" : "ar", &ar); - } - - // build arguments - std::vector args; - - // ask ar to create a new library - if (!isTargetMSVC) { - args.push_back("rcs"); - } - - // ask lib to be quiet - if (isTargetMSVC) { - args.push_back("/NOLOGO"); - } - - // output filename - std::string libName; - if (global.params.libname) { // explicit - // DMD adds the default extension if there is none - libName = opts::invokedByLDMD - ? FileName::defaultExt(global.params.libname, global.lib_ext) - : global.params.libname; - } else { // infer from first object file - libName = global.params.objfiles->dim - ? FileName::removeExt((*global.params.objfiles)[0]) - : "a.out"; - libName += '.'; - libName += global.lib_ext; - } - - // DMD creates static libraries in the objects directory (unless using an - // absolute output path via `-of`). - if (opts::invokedByLDMD && global.params.objdir && - !FileName::absolute(libName.c_str())) { - libName = FileName::combine(global.params.objdir, libName.c_str()); - } - - if (isTargetMSVC) { - args.push_back("/OUT:" + libName); - } else { - args.push_back(libName); - } - - appendObjectFiles(args); - - // create path to the library - CreateDirectoryOnDisk(libName); - -#if LDC_LLVM_VER >= 309 - if (useInternalArchiver) { - std::vector fullArgs; - fullArgs.reserve(1 + args.size()); - fullArgs.push_back(tool.c_str()); - for (const auto &arg : args) - fullArgs.push_back(arg.c_str()); - - if (global.params.verbose) { - for (auto arg : fullArgs) { - fprintf(global.stdmsg, "%s ", arg); - } - fprintf(global.stdmsg, "\n"); - fflush(global.stdmsg); - } - - const int exitCode = isTargetMSVC ? ldc::lib(fullArgs) : ldc::ar(fullArgs); - if (exitCode) - error(Loc(), "%s failed with status: %d", tool.c_str(), exitCode); - - return exitCode; - } -#endif - - // try to call archiver - return executeToolAndWait(tool, args, global.params.verbose); + return linkObjToBinaryGcc(gExePath, useInternalLinker, staticFlag); } ////////////////////////////////////////////////////////////////////////////// @@ -778,7 +162,6 @@ void deleteExeFile() { int runProgram() { assert(!gExePath.empty()); - // assert(gExePath.isValid()); // Run executable int status = diff --git a/driver/linker.h b/driver/linker.h index 9c6d9c09680..e73189301ab 100644 --- a/driver/linker.h +++ b/driver/linker.h @@ -34,12 +34,6 @@ void insertBitcodeFiles(llvm::Module &M, llvm::LLVMContext &Ctx, */ int linkObjToBinary(); -/** - * Create a static library from object files. - * @return 0 on success. - */ -int createStaticLibrary(); - /** * Delete the executable that was previously linked with linkObjToBinary. */ diff --git a/driver/main.cpp b/driver/main.cpp index 40a9b7527ae..7e4454147ad 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -404,8 +404,8 @@ void parseCommandLine(int argc, char **argv, Strings &sourceFiles, const auto toWinPaths = [](Strings *paths) { if (!paths) return; - for (unsigned i = 0; i < paths->dim; ++i) - (*paths)[i] = dupPathString((*paths)[i]); + for (auto &path : *paths) + path = dupPathString(path); }; toWinPaths(global.params.imppath); toWinPaths(global.params.fileImppath); diff --git a/driver/tool.cpp b/driver/tool.cpp index 8d54b214601..5f5bd39381b 100644 --- a/driver/tool.cpp +++ b/driver/tool.cpp @@ -13,6 +13,7 @@ #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/Program.h" #ifdef _WIN32 #include @@ -73,6 +74,43 @@ std::string getGcc() { #endif } +////////////////////////////////////////////////////////////////////////////// + +void createDirectoryForFileOrFail(llvm::StringRef fileName) { + auto dir = llvm::sys::path::parent_path(fileName); + if (!dir.empty() && !llvm::sys::fs::exists(dir)) { + if (auto ec = llvm::sys::fs::create_directories(dir)) { + error(Loc(), "failed to create path to file: %s\n%s", dir.data(), + ec.message().c_str()); + fatal(); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// + +std::vector getFullArgs(const std::string &tool, + const std::vector &args, + bool printVerbose) { + std::vector fullArgs; + fullArgs.reserve(args.size() + + 2); // executeToolAndWait() appends an additional null + + fullArgs.push_back(tool.c_str()); + for (const auto &arg : args) + fullArgs.push_back(arg.c_str()); + + // Print command line if requested + if (printVerbose) { + for (auto arg : fullArgs) + fprintf(global.stdmsg, "%s ", arg); + fprintf(global.stdmsg, "\n"); + fflush(global.stdmsg); + } + + return fullArgs; +} + //////////////////////////////////////////////////////////////////////////////// int executeToolAndWait(const std::string &tool_, @@ -85,24 +123,9 @@ int executeToolAndWait(const std::string &tool_, // Construct real argument list. // First entry is the tool itself, last entry must be NULL. - std::vector realargs; - realargs.reserve(args.size() + 2); - realargs.push_back(tool.c_str()); - for (const auto &arg : args) { - realargs.push_back(arg.c_str()); - } + auto realargs = getFullArgs(tool, args, verbose); realargs.push_back(nullptr); - // Print command line if requested - if (verbose) { - // Print it - for (size_t i = 0; i < realargs.size() - 1; i++) { - fprintf(global.stdmsg, "%s ", realargs[i]); - } - fprintf(global.stdmsg, "\n"); - fflush(global.stdmsg); - } - // Execute tool. std::string errstr; if (int status = llvm::sys::ExecuteAndWait(tool, &realargs[0], nullptr, diff --git a/driver/tool.h b/driver/tool.h index 448262106fa..ef4d1e5f206 100644 --- a/driver/tool.h +++ b/driver/tool.h @@ -26,6 +26,12 @@ std::string getProgram(const char *name, const llvm::cl::opt *opt = nullptr, const char *envVar = nullptr); +void createDirectoryForFileOrFail(llvm::StringRef fileName); + +std::vector getFullArgs(const std::string &tool, + const std::vector &args, + bool printVerbose); + int executeToolAndWait(const std::string &tool, std::vector const &args, bool verbose = false); diff --git a/gen/asmstmt.cpp b/gen/asmstmt.cpp index ccba4aa3e71..29c36476c5f 100644 --- a/gen/asmstmt.cpp +++ b/gen/asmstmt.cpp @@ -480,8 +480,7 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) { // do asm statements for (unsigned i = 0; i < stmt->statements->dim; i++) { - Statement *s = (*stmt->statements)[i]; - if (s) { + if (Statement *s = (*stmt->statements)[i]) { Statement_toIR(s, p); } } diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp index 79267b82483..03fca6330cf 100644 --- a/gen/dibuilder.cpp +++ b/gen/dibuilder.cpp @@ -1177,11 +1177,9 @@ void ldc::DIBuilder::EmitLocalVariable(llvm::Value *ll, VarDeclaration *vd, size_t argNo = 0; if (fd->vthis != vd) { assert(fd->parameters); - for (argNo = 0; argNo < fd->parameters->dim; argNo++) { - if ((*fd->parameters)[argNo] == vd) - break; - } - assert(argNo < fd->parameters->dim); + auto it = std::find(fd->parameters->begin(), fd->parameters->end(), vd); + assert(it != fd->parameters->end()); + argNo = it - fd->parameters->begin(); if (fd->vthis) argNo++; } diff --git a/gen/inlineir.cpp b/gen/inlineir.cpp index bb87e1bfd6a..8a7f286c05c 100644 --- a/gen/inlineir.cpp +++ b/gen/inlineir.cpp @@ -165,11 +165,10 @@ DValue *DtoInlineIRExpr(Loc &loc, FuncDeclaration *fdecl, fun->setCallingConv(llvm::CallingConv::C); // Build the runtime arguments - size_t n = arguments->dim; llvm::SmallVector args; - args.reserve(n); - for (size_t i = 0; i < n; i++) { - args.push_back(DtoRVal((*arguments)[i])); + args.reserve(arguments->dim); + for (auto arg : *arguments) { + args.push_back(DtoRVal(arg)); } llvm::Value *rv = gIR->ir->CreateCall(fun, args); diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 6e8909f6f4b..0c912304da9 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -988,8 +988,7 @@ DValue *DtoDeclarationExp(Dsymbol *declaration) { } else if (AttribDeclaration *a = declaration->isAttribDeclaration()) { Logger::println("AttribDeclaration"); // choose the right set in case this is a conditional declaration - Dsymbols *d = a->include(nullptr, nullptr); - if (d) { + if (auto d = a->include(nullptr, nullptr)) { for (unsigned i = 0; i < d->dim; ++i) { DtoDeclarationExp((*d)[i]); } @@ -1004,7 +1003,7 @@ DValue *DtoDeclarationExp(Dsymbol *declaration) { assert(tupled->isexp && "Non-expression tuple decls not handled yet."); assert(tupled->objects); for (unsigned i = 0; i < tupled->objects->dim; ++i) { - DsymbolExp *exp = static_cast(tupled->objects->data[i]); + auto exp = static_cast((*tupled->objects)[i]); DtoDeclarationExp(exp->s); } } else { diff --git a/gen/mangling.cpp b/gen/mangling.cpp index ef312009e41..a9c76898b7f 100644 --- a/gen/mangling.cpp +++ b/gen/mangling.cpp @@ -58,8 +58,8 @@ std::string hashSymbolName(llvm::StringRef name, Dsymbol *symb) { auto moddecl = symb->getModule()->md; assert(moddecl); if (auto packages = moddecl->packages) { - for (size_t i = 0; i < packages->dim; ++i) { - llvm::StringRef str = (*packages)[i]->toChars(); + for (auto package : *packages) { + llvm::StringRef str = package->toChars(); ret += std::to_string(str.size()); ret += str; } diff --git a/gen/modules.cpp b/gen/modules.cpp index f4105a4867f..a3428cd9a06 100644 --- a/gen/modules.cpp +++ b/gen/modules.cpp @@ -718,6 +718,7 @@ void codegenModule(IRState *irs, Module *m) { } // process module members + // NOTE: m->members may grow during codegen for (unsigned k = 0; k < m->members->dim; k++) { Dsymbol *dsym = (*m->members)[k]; assert(dsym); diff --git a/gen/statements.cpp b/gen/statements.cpp index 4b160741c67..056e81cb2a7 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -1210,7 +1210,7 @@ class ToIRVisitor : public Visitor { } // do statements - Statement **stmts = static_cast(stmt->statements->data); + Statement **stmts = stmt->statements->data; for (size_t i = 0; i < nstmt; i++) { Statement *s = stmts[i]; diff --git a/gen/toconstelem.cpp b/gen/toconstelem.cpp index 481e50205b6..a6652fba182 100644 --- a/gen/toconstelem.cpp +++ b/gen/toconstelem.cpp @@ -591,8 +591,8 @@ class ToConstElemVisitor : public Visitor { std::map varInits; const size_t nexprs = e->elements->dim; for (size_t i = 0; i < nexprs; i++) { - if ((*e->elements)[i]) { - LLConstant *c = toConstElem((*e->elements)[i]); + if (auto elem = (*e->elements)[i]) { + LLConstant *c = toConstElem(elem); // extend i1 to i8 if (c->getType() == LLType::getInt1Ty(p->context())) c = llvm::ConstantExpr::getZExt(c, LLType::getInt8Ty(p->context())); @@ -641,12 +641,12 @@ class ToConstElemVisitor : public Visitor { cur = classHierachy.top(); classHierachy.pop(); for (size_t j = 0; j < cur->fields.dim; ++j) { - if ((*value->elements)[i]) { + if (auto elem = (*value->elements)[i]) { VarDeclaration *field = cur->fields[j]; IF_LOG Logger::println("Getting initializer for: %s", field->toChars()); LOG_SCOPE; - varInits[field] = toConstElem((*value->elements)[i]); + varInits[field] = toConstElem(elem); } ++i; } diff --git a/gen/toir.cpp b/gen/toir.cpp index f970952c629..b4764e0a3d5 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -1466,8 +1466,8 @@ class ToElemVisitor : public Visitor { size_t ndims = e->arguments->dim; std::vector dims; dims.reserve(ndims); - for (size_t i = 0; i < ndims; ++i) { - dims.push_back(toElem((*e->arguments)[i])); + for (auto arg : *e->arguments) { + dims.push_back(toElem(arg)); } result = DtoNewMulDimDynArray(e->loc, e->newtype, &dims[0], ndims); } @@ -2272,7 +2272,7 @@ class ToElemVisitor : public Visitor { auto global = new llvm::GlobalVariable( gIR->module, init->getType(), true, llvm::GlobalValue::InternalLinkage, init, ".immutablearray"); - result = new DSliceValue(arrayType, DtoConstSize_t(e->elements->dim), + result = new DSliceValue(arrayType, DtoConstSize_t(len), DtoBitCast(global, getPtrToType(llElemType))); } else { DSliceValue *dynSlice = DtoNewDynArray( @@ -2431,8 +2431,8 @@ class ToElemVisitor : public Visitor { keysInits.reserve(e->keys->dim); valuesInits.reserve(e->keys->dim); for (size_t i = 0, n = e->keys->dim; i < n; ++i) { - Expression *ekey = e->keys->tdata()[i]; - Expression *eval = e->values->tdata()[i]; + Expression *ekey = (*e->keys)[i]; + Expression *eval = (*e->values)[i]; IF_LOG Logger::println("(%llu) aa[%s] = %s", static_cast(i), ekey->toChars(), eval->toChars()); @@ -2585,8 +2585,8 @@ class ToElemVisitor : public Visitor { std::vector types; types.reserve(e->exps->dim); - for (size_t i = 0; i < e->exps->dim; i++) { - types.push_back(DtoMemType((*e->exps)[i]->type)); + for (auto exp : *e->exps) { + types.push_back(DtoMemType(exp->type)); } LLValue *val = DtoRawAlloca(LLStructType::get(gIR->context(), types), 0, ".tuple"); diff --git a/gen/typinf.cpp b/gen/typinf.cpp index a63cbf02422..5747b017e9b 100644 --- a/gen/typinf.cpp +++ b/gen/typinf.cpp @@ -486,8 +486,7 @@ class LLVMDefineVisitor : public Visitor { LLType *tiTy = DtoType(Type::dtypeinfo->type); - for (size_t i = 0; i < dim; i++) { - Parameter *arg = static_cast(tu->arguments->data[i]); + for (auto arg : *tu->arguments) { arrInits.push_back(DtoTypeInfoOf(arg->type, true)); }