[pkg-d-commits] [ldc] 131/211: Add LTO support (full and thin), with `-flto=thin|full`.
Matthias Klumpp
mak at moszumanska.debian.org
Sun Apr 23 22:36:16 UTC 2017
This is an automated email from the git hooks/post-receive script.
mak pushed a commit to annotated tag v1.1.0
in repository ldc.
commit bb24fb281626cc2ba127296f9bc941c603aea7e6
Author: Johan Engelen <jbc.engelen at gmail.com>
Date: Sun Oct 16 20:48:31 2016 +0200
Add LTO support (full and thin), with `-flto=thin|full`.
LTO needs linker support: I am only aware of support on OS X and Linux (through the LLVMgold plugin).
Resolves #693
---
CMakeLists.txt | 13 ++++
driver/cl_options.cpp | 10 +++
driver/cl_options.h | 15 +++++
driver/exe_path.cpp | 12 ++++
driver/exe_path.h | 2 +
driver/linker.cpp | 118 +++++++++++++++++++++++++++++++++
driver/toobj.cpp | 75 +++++++++++++++++----
gen/optimizer.cpp | 2 +-
gen/optimizer.h | 2 +
tests/linking/fulllto_1.d | 14 ++++
tests/linking/inputs/asm_x86.d | 13 ++++
tests/linking/inputs/thinlto_ctor.d | 6 ++
tests/linking/thinlto_1.d | 14 ++++
tests/linking/thinlto_asm_x86.d | 13 ++++
tests/linking/thinlto_modulecdtors.d | 26 ++++++++
tests/linking/thinlto_modulecdtors_2.d | 19 ++++++
tests/lit.site.cfg.in | 21 ++++++
17 files changed, 361 insertions(+), 14 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0b911c4..5a84f4b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -718,6 +718,19 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
install(DIRECTORY bash_completion.d/ DESTINATION ${BASH_COMPLETION_COMPLETIONSDIR})
endif()
+# Also install LLVM's LTO binary if available
+if(APPLE)
+ set(LLVM_LTO_BINARY ${LLVM_LIBRARY_DIRS}/libLTO.dylib)
+elseif(UNIX)
+ set(LLVM_LTO_BINARY ${LLVM_LIBRARY_DIRS}/LLVMgold.so)
+endif()
+if(EXISTS ${LLVM_LTO_BINARY})
+ message(STATUS "Also installing LTO binary: ${LLVM_LTO_BINARY}")
+ install(PROGRAMS ${LLVM_LTO_BINARY} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)
+ file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
+ configure_file(${LLVM_LTO_BINARY} ${PROJECT_BINARY_DIR}/lib COPYONLY)
+endif()
+
#
# Packaging
#
diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp
index b7be19b..51f185a 100644
--- a/driver/cl_options.cpp
+++ b/driver/cl_options.cpp
@@ -460,6 +460,16 @@ cl::opt<bool>
cl::desc("Instrument function entry and exit with "
"GCC-compatible profiling calls"));
+#if LDC_LLVM_VER >= 309
+cl::opt<LTOKind> ltoMode(
+ "flto", cl::desc("Set LTO mode, requires linker support"),
+ cl::init(LTO_None),
+ clEnumValues(
+ clEnumValN(LTO_Full, "full", "Merges all input into a single module"),
+ clEnumValN(LTO_Thin, "thin",
+ "Parallel importing and codegen (faster than 'full')")));
+#endif
+
static cl::extrahelp footer(
"\n"
"-d-debug can also be specified without options, in which case it enables "
diff --git a/driver/cl_options.h b/driver/cl_options.h
index a2b8b49..46ecf18 100644
--- a/driver/cl_options.h
+++ b/driver/cl_options.h
@@ -96,5 +96,20 @@ extern std::vector<std::string> debugArgs;
#if LDC_LLVM_VER >= 307
void CreateColorOption();
#endif
+
+#if LDC_LLVM_VER >= 309
+// LTO options
+enum LTOKind {
+ LTO_None,
+ LTO_Full,
+ LTO_Thin,
+};
+extern cl::opt<LTOKind> ltoMode;
+inline bool isUsingLTO() { return ltoMode != LTO_None; }
+inline bool isUsingThinLTO() { return ltoMode == LTO_Thin; }
+#else
+inline bool isUsingLTO() { return false; }
+inline bool isUsingThinLTO() { return false; }
+#endif
}
#endif
diff --git a/driver/exe_path.cpp b/driver/exe_path.cpp
index f798fcb..6d18b23 100644
--- a/driver/exe_path.cpp
+++ b/driver/exe_path.cpp
@@ -46,8 +46,20 @@ string exe_path::getBaseDir() {
return path::parent_path(binDir);
}
+string exe_path::getLibDir() {
+ llvm::SmallString<128> r(getBaseDir());
+ path::append(r, "lib");
+ return r.str();
+}
+
string exe_path::prependBinDir(const char *suffix) {
llvm::SmallString<128> r(getBinDir());
path::append(r, suffix);
return r.str();
}
+
+string exe_path::prependLibDir(const char *suffix) {
+ llvm::SmallString<128> r(getLibDir());
+ path::append(r, suffix);
+ return r.str();
+}
diff --git a/driver/exe_path.h b/driver/exe_path.h
index 7aa682b..ea30dd7 100644
--- a/driver/exe_path.h
+++ b/driver/exe_path.h
@@ -24,7 +24,9 @@ void initialize(const char *arg0);
const std::string &getExePath(); // <baseDir>/bin/ldc2
std::string getBinDir(); // <baseDir>/bin
std::string getBaseDir(); // <baseDir>
+std::string getLibDir(); // <baseDir>/lib
std::string prependBinDir(const char *suffix); // <baseDir>/bin/<suffix>
+std::string prependLibDir(const char *suffix); // <baseDir>/lib/<suffix>
}
#endif // LDC_DRIVER_EXE_PATH_H
diff --git a/driver/linker.cpp b/driver/linker.cpp
index 8eeec17..b380331 100644
--- a/driver/linker.cpp
+++ b/driver/linker.cpp
@@ -14,6 +14,7 @@
#include "driver/cl_options.h"
#include "driver/exe_path.h"
#include "driver/tool.h"
+#include "gen/irstate.h"
#include "gen/llvm.h"
#include "gen/logger.h"
#include "gen/optimizer.h"
@@ -26,12 +27,16 @@
#include "llvm/Support/Program.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SourceMgr.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Target/TargetOptions.h"
#if _WIN32
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/ConvertUTF.h"
#include <Windows.h>
#endif
+#include <algorithm>
+
//////////////////////////////////////////////////////////////////////////////
static llvm::cl::opt<bool> staticFlag(
@@ -46,6 +51,12 @@ static llvm::cl::opt<bool> createStaticLibInObjdir(
llvm::cl::desc("Create static library in -od directory (DMD-compliant)"),
llvm::cl::ZeroOrMore, llvm::cl::ReallyHidden);
+static llvm::cl::opt<std::string> ltoLibrary(
+ "flto-binary",
+ llvm::cl::desc(
+ "Set the path for LLVMgold.so (Unixes) or libLTO.dylib (Darwin)"),
+ llvm::cl::value_desc("file"));
+
//////////////////////////////////////////////////////////////////////////////
static void CreateDirectoryOnDisk(llvm::StringRef fileName) {
@@ -100,6 +111,108 @@ static std::string getOutputName(bool const sharedLib) {
}
//////////////////////////////////////////////////////////////////////////////
+// LTO functionality
+
+namespace {
+
+void addLinkerFlag(std::vector<std::string> &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[] = {
+ exe_path::prependLibDir("LLVMgold.so"), "/usr/local/lib/LLVMgold.so",
+ "/usr/lib/bfd-plugins/LLVMgold.so",
+ };
+
+ // Try all searchPaths and early return upon the first path found.
+ for (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=<file>.");
+ fatal();
+ }
+}
+
+void addLTOGoldPluginFlags(std::vector<std::string> &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<char>(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 {
+ std::string searchPath = exe_path::prependLibDir("libLTO.dylib");
+ if (llvm::sys::fs::exists(searchPath))
+ return searchPath;
+
+ return "";
+ }
+}
+
+void addDarwinLTOFlags(std::vector<std::string> &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<std::string> &args) {
+#if LDC_LLVM_VER >= 309
+ 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);
+ }
+#endif
+}
+} // anonymous namespace
+
+//////////////////////////////////////////////////////////////////////////////
namespace {
@@ -223,6 +336,11 @@ static int linkObjToBinaryGcc(bool sharedLib, bool fullyStatic) {
args.push_back("-fsanitize=thread");
}
+ // 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);
+
// additional linker switches
for (unsigned i = 0; i < global.params.linkswitches->dim; i++) {
const char *p = (*global.params.linkswitches)[i];
diff --git a/driver/toobj.cpp b/driver/toobj.cpp
index e5a8557..72425bb 100644
--- a/driver/toobj.cpp
+++ b/driver/toobj.cpp
@@ -19,6 +19,9 @@
#include "gen/programs.h"
#include "llvm/IR/AssemblyAnnotationWriter.h"
#include "llvm/IR/Verifier.h"
+#if LDC_LLVM_VER >= 309
+#include "llvm/Analysis/ModuleSummaryAnalysis.h"
+#endif
#include "llvm/Bitcode/ReaderWriter.h"
#if LDC_LLVM_VER >= 307
#include "llvm/IR/LegacyPassManager.h"
@@ -364,20 +367,47 @@ void writeObjectFile(llvm::Module *m, std::string &filename) {
}
}
}
-} // end of anonymous namespace
-void writeModule(llvm::Module *m, std::string filename) {
+bool shouldAssembleExternally() {
// There is no integrated assembler on AIX because XCOFF is not supported.
// Starting with LLVM 3.5 the integrated assembler can be used with MinGW.
- bool const assembleExternally =
- global.params.output_o &&
- (NoIntegratedAssembler ||
- global.params.targetTriple->getOS() == llvm::Triple::AIX);
+ return global.params.output_o &&
+ (NoIntegratedAssembler ||
+ global.params.targetTriple->getOS() == llvm::Triple::AIX);
+}
+
+bool shouldOutputObjectFile() {
+ return global.params.output_o && !shouldAssembleExternally();
+}
- // Use cached object code if possible
- bool useIR2ObjCache = !opts::cacheDir.empty();
+bool shouldDoLTO(llvm::Module *m) {
+#if LDC_LLVM_VER < 309
+ return false;
+#else
+#if LDC_LLVM_VER == 309
+ // LLVM 3.9 bug: can't do ThinLTO with modules that have module-scope inline
+ // assembly blocks (duplicate definitions upon importing from such a module).
+ // https://llvm.org/bugs/show_bug.cgi?id=30610
+ if (opts::isUsingThinLTO() && !m->getModuleInlineAsm().empty())
+ return false;
+#endif
+ return opts::isUsingLTO();
+#endif
+}
+} // end of anonymous namespace
+
+void writeModule(llvm::Module *m, std::string filename) {
+ const bool doLTO = shouldDoLTO(m);
+ const bool outputObj = shouldOutputObjectFile();
+ const bool assembleExternally = shouldAssembleExternally();
+
+ // Use cached object code if possible.
+ // TODO: combine LDC's cache and LTO (the advantage is skipping the IR
+ // optimization).
+ const bool useIR2ObjCache =
+ !opts::cacheDir.empty() && outputObj && !doLTO;
llvm::SmallString<32> moduleHash;
- if (useIR2ObjCache && global.params.output_o && !assembleExternally) {
+ if (useIR2ObjCache) {
llvm::SmallString<128> cacheDir(opts::cacheDir.c_str());
llvm::sys::fs::make_absolute(cacheDir);
opts::cacheDir = cacheDir.c_str();
@@ -411,9 +441,10 @@ void writeModule(llvm::Module *m, std::string filename) {
}
// write LLVM bitcode
- if (global.params.output_bc) {
+ if (global.params.output_bc || (doLTO && outputObj)) {
LLPath bcpath(filename);
- llvm::sys::path::replace_extension(bcpath, global.bc_ext);
+ if (global.params.output_bc)
+ llvm::sys::path::replace_extension(bcpath, global.bc_ext);
Logger::println("Writing LLVM bitcode to: %s\n", bcpath.c_str());
LLErrorInfo errinfo;
llvm::raw_fd_ostream bos(bcpath.c_str(), errinfo, llvm::sys::fs::F_None);
@@ -422,7 +453,25 @@ void writeModule(llvm::Module *m, std::string filename) {
ERRORINFO_STRING(errinfo));
fatal();
}
- llvm::WriteBitcodeToFile(m, bos);
+ if (opts::isUsingThinLTO()) {
+#if LDC_LLVM_VER >= 309
+ Logger::println("Creating module summary for ThinLTO");
+#if LDC_LLVM_VER == 309
+ // TODO: add PGO data in here when available (function freq info).
+ llvm::ModuleSummaryIndexBuilder indexBuilder(m, nullptr);
+ auto &moduleSummaryIndex = indexBuilder.getIndex();
+#else
+ // TODO: add PGO data in here when available (function freq info and
+ // profile summary info).
+ auto moduleSummaryIndex = buildModuleSummaryIndex(*m, nullptr, nullptr);
+#endif
+
+ llvm::WriteBitcodeToFile(m, bos, true, &moduleSummaryIndex,
+ /* generate ThinLTO hash */ true);
+#endif
+ } else {
+ llvm::WriteBitcodeToFile(m, bos);
+ }
}
// write LLVM IR
@@ -476,7 +525,7 @@ void writeModule(llvm::Module *m, std::string filename) {
}
}
- if (global.params.output_o && !assembleExternally) {
+ if (outputObj && !doLTO) {
writeObjectFile(m, filename);
if (useIR2ObjCache) {
cache::cacheObjectFile(filename, moduleHash);
diff --git a/gen/optimizer.cpp b/gen/optimizer.cpp
index ebe9aae..db96050 100644
--- a/gen/optimizer.cpp
+++ b/gen/optimizer.cpp
@@ -125,7 +125,7 @@ static cl::opt<bool>
cl::desc("Disable the slp vectorization pass"),
cl::init(false));
-static unsigned optLevel() {
+unsigned optLevel() {
// Use -O2 as a base for the size-optimization levels.
return optimizeLevel >= 0 ? optimizeLevel : 2;
}
diff --git a/gen/optimizer.h b/gen/optimizer.h
index 8a2b6c0..da6d34a 100644
--- a/gen/optimizer.h
+++ b/gen/optimizer.h
@@ -47,6 +47,8 @@ bool willInline();
bool willCrossModuleInline();
+unsigned optLevel();
+
bool isOptimizationEnabled();
llvm::CodeGenOpt::Level codeGenOptLevel();
diff --git a/tests/linking/fulllto_1.d b/tests/linking/fulllto_1.d
new file mode 100644
index 0000000..a847b56
--- /dev/null
+++ b/tests/linking/fulllto_1.d
@@ -0,0 +1,14 @@
+// Test full LTO commandline flag
+
+// REQUIRES: atleast_llvm309
+// REQUIRES: LTO
+
+// RUN: %ldc %s -of=%t%obj -c -flto=full -vv | FileCheck %s
+// RUN: %ldc -flto=full -run %s
+
+// CHECK: Writing LLVM bitcode
+// CHECK-NOT: Creating module summary
+
+void main()
+{
+}
diff --git a/tests/linking/inputs/asm_x86.d b/tests/linking/inputs/asm_x86.d
new file mode 100644
index 0000000..a5bdd4a
--- /dev/null
+++ b/tests/linking/inputs/asm_x86.d
@@ -0,0 +1,13 @@
+void foo()
+{
+ asm
+ {
+ naked;
+ ret;
+ }
+}
+
+int simplefunction()
+{
+ return 1;
+}
diff --git a/tests/linking/inputs/thinlto_ctor.d b/tests/linking/inputs/thinlto_ctor.d
new file mode 100644
index 0000000..3a0f996
--- /dev/null
+++ b/tests/linking/inputs/thinlto_ctor.d
@@ -0,0 +1,6 @@
+import core.stdc.stdio;
+
+static this()
+{
+ puts("ctor\n");
+}
diff --git a/tests/linking/thinlto_1.d b/tests/linking/thinlto_1.d
new file mode 100644
index 0000000..abd19bf
--- /dev/null
+++ b/tests/linking/thinlto_1.d
@@ -0,0 +1,14 @@
+// Test ThinLTO commandline flag
+
+// REQUIRES: atleast_llvm309
+// REQUIRES: LTO
+
+// RUN: %ldc %s -of=%t%obj -c -flto=thin -vv | FileCheck %s
+// RUN: %ldc -flto=thin -run %s
+
+// CHECK: Writing LLVM bitcode
+// CHECK: Creating module summary for ThinLTO
+
+void main()
+{
+}
diff --git a/tests/linking/thinlto_asm_x86.d b/tests/linking/thinlto_asm_x86.d
new file mode 100644
index 0000000..b9842c7
--- /dev/null
+++ b/tests/linking/thinlto_asm_x86.d
@@ -0,0 +1,13 @@
+// ThinLTO: Test inline assembly functions with thinlto
+
+// REQUIRES: atleast_llvm309
+// REQUIRES: LTO
+
+// RUN: %ldc -flto=thin %S/inputs/asm_x86.d -c -of=%t_input%obj
+// RUN: %ldc -flto=thin -I%S %s %t_input%obj
+
+import inputs.asm_x86;
+
+int main() {
+ return simplefunction();
+}
diff --git a/tests/linking/thinlto_modulecdtors.d b/tests/linking/thinlto_modulecdtors.d
new file mode 100644
index 0000000..4ef8560
--- /dev/null
+++ b/tests/linking/thinlto_modulecdtors.d
@@ -0,0 +1,26 @@
+// ThinLTO: Test that module ctors/dtors are called
+
+// REQUIRES: atleast_llvm309
+// REQUIRES: LTO
+
+// RUN: %ldc -flto=thin -O3 -run %s | FileCheck %s
+
+// CHECK: ctor
+// CHECK: main
+// CHECK: dtor
+
+import core.stdc.stdio;
+
+static this()
+{
+ puts("ctor\n");
+}
+
+static ~this()
+{
+ puts("dtor\n");
+}
+
+void main() {
+ puts("main\n");
+}
diff --git a/tests/linking/thinlto_modulecdtors_2.d b/tests/linking/thinlto_modulecdtors_2.d
new file mode 100644
index 0000000..e29730f
--- /dev/null
+++ b/tests/linking/thinlto_modulecdtors_2.d
@@ -0,0 +1,19 @@
+// REQUIRES: atleast_llvm309
+// REQUIRES: LTO
+
+// RUN: %ldc -flto=thin -O3 %S/inputs/thinlto_ctor.d -run %s | FileCheck --check-prefix=EXECUTE %s
+
+// EXECUTE: ctor
+// EXECUTE: main
+// EXECUTE: dtor
+
+import core.stdc.stdio;
+
+static ~this()
+{
+ puts("dtor\n");
+}
+
+void main() {
+ puts("main\n");
+}
diff --git a/tests/lit.site.cfg.in b/tests/lit.site.cfg.in
index a47558e..9ebd30e 100644
--- a/tests/lit.site.cfg.in
+++ b/tests/lit.site.cfg.in
@@ -3,6 +3,7 @@ import os
import sys
import platform
import string
+import subprocess
## Auto-initialized variables by cmake:
config.ldc2_bin = "@LDC2_BIN@"
@@ -66,6 +67,26 @@ if (platform.system() == 'Windows') and (config.default_target_bits == 32):
if (platform.system() == 'Windows') and (config.default_target_bits == 64):
config.available_features.add('Windows_x64')
+# Add "LTO" feature if linker support is available (LTO is supported from LLVM 3.9)
+canDoLTO = False
+if (config.llvm_version >= 309):
+ if (platform.system() == 'Darwin'):
+ command = ['ld', '-v']
+ p = subprocess.Popen(command, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, universal_newlines=True)
+ text = p.stderr.read()
+ if "LTO support" in text:
+ canDoLTO = True
+ elif (platform.system() == 'Linux'):
+ command = ['ld', '-plugin']
+ p = subprocess.Popen(command, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, universal_newlines=True)
+ text = p.stderr.read()
+ if "plugin: missing argument" in text:
+ canDoLTO = True
+if canDoLTO:
+ config.available_features.add('LTO')
+
config.target_triple = '(unused)'
# test_exec_root: The root path where tests should be run.
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-d/ldc.git
More information about the pkg-d-commits
mailing list