[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