[llvm] [RFC][LTO] Allow target-specific module splittting (PR #83128)

Pierre van Houtryve via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 19 01:44:09 PDT 2024


https://github.com/Pierre-vh updated https://github.com/llvm/llvm-project/pull/83128

>From 6bb50651090d6dc474198491b8aa4b2f6dad8dda Mon Sep 17 00:00:00 2001
From: pvanhout <pierre.vanhoutryve at amd.com>
Date: Tue, 27 Feb 2024 13:26:18 +0100
Subject: [PATCH 1/2] [RFC][LTO] Allow target-specific module splittting

Allow targets to implement custom module splitting logic for
--lto-partitions.
---
 llvm/include/llvm/Target/TargetMachine.h |  12 +++
 llvm/lib/LTO/LTOBackend.cpp              |  13 ++-
 llvm/tools/llvm-split/CMakeLists.txt     |   7 ++
 llvm/tools/llvm-split/llvm-split.cpp     | 101 +++++++++++++++++------
 4 files changed, 104 insertions(+), 29 deletions(-)

diff --git a/llvm/include/llvm/Target/TargetMachine.h b/llvm/include/llvm/Target/TargetMachine.h
index ceb371bdc73480..48ea3cfe02775b 100644
--- a/llvm/include/llvm/Target/TargetMachine.h
+++ b/llvm/include/llvm/Target/TargetMachine.h
@@ -418,6 +418,18 @@ class TargetMachine {
   virtual unsigned getAddressSpaceForPseudoSourceKind(unsigned Kind) const {
     return 0;
   }
+
+  /// Entry point for module splitting. Targets can implement custom module
+  /// splitting logic, mainly used by LTO for --lto-partitions.
+  ///
+  /// \returns `true` if the module was split, `false` otherwise. When  `false`
+  /// is returned, it is assumed that \p ModuleCallback has never been called
+  /// and \p M has not been modified.
+  virtual bool splitModule(
+      Module &M, unsigned NumParts,
+      function_ref<void(std::unique_ptr<Module> MPart)> ModuleCallback) const {
+    return false;
+  }
 };
 
 /// This class describes a target machine that is implemented with the LLVM
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index 71e8849dc3cc91..d4b89ede2d7134 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -436,8 +436,7 @@ static void splitCodeGen(const Config &C, TargetMachine *TM,
   unsigned ThreadCount = 0;
   const Target *T = &TM->getTarget();
 
-  SplitModule(
-      Mod, ParallelCodeGenParallelismLevel,
+  const auto HandleModulePartition =
       [&](std::unique_ptr<Module> MPart) {
         // We want to clone the module in a new context to multi-thread the
         // codegen. We do it by serializing partition modules to bitcode
@@ -469,8 +468,14 @@ static void splitCodeGen(const Config &C, TargetMachine *TM,
             // Pass BC using std::move to ensure that it get moved rather than
             // copied into the thread's context.
             std::move(BC), ThreadCount++);
-      },
-      false);
+      };
+
+  // Try target-specific module splitting first, then fallback to the default.
+  if (!TM->splitModule(Mod, ParallelCodeGenParallelismLevel,
+                       HandleModulePartition)) {
+    SplitModule(Mod, ParallelCodeGenParallelismLevel, HandleModulePartition,
+                false);
+  }
 
   // Because the inner lambda (which runs in a worker thread) captures our local
   // variables, we need to wait for the worker threads to terminate before we
diff --git a/llvm/tools/llvm-split/CMakeLists.txt b/llvm/tools/llvm-split/CMakeLists.txt
index 52eedeb9f53f32..0579298462d113 100644
--- a/llvm/tools/llvm-split/CMakeLists.txt
+++ b/llvm/tools/llvm-split/CMakeLists.txt
@@ -1,9 +1,16 @@
 set(LLVM_LINK_COMPONENTS
+  AllTargetsAsmParsers
+  AllTargetsCodeGens
+  AllTargetsDescs
+  AllTargetsInfos
   TransformUtils
   BitWriter
+  CodeGen
   Core
   IRReader
+  MC
   Support
+  Target
   )
 
 add_llvm_tool(llvm-split
diff --git a/llvm/tools/llvm-split/llvm-split.cpp b/llvm/tools/llvm-split/llvm-split.cpp
index c6e20e0373c717..252e04e77941f2 100644
--- a/llvm/tools/llvm-split/llvm-split.cpp
+++ b/llvm/tools/llvm-split/llvm-split.cpp
@@ -1,4 +1,4 @@
-//===-- llvm-split: command line tool for testing module splitter ---------===//
+//===-- llvm-split: command line tool for testing module splitting --------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,7 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This program can be used to test the llvm::SplitModule function.
+// This program can be used to test the llvm::SplitModule and
+// TargetMachine::splitModule functions.
 //
 //===----------------------------------------------------------------------===//
 
@@ -15,12 +16,17 @@
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Verifier.h"
 #include "llvm/IRReader/IRReader.h"
+#include "llvm/MC/TargetRegistry.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
 #include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
 #include "llvm/Support/ToolOutputFile.h"
-#include "llvm/Support/raw_ostream.h"
 #include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/TargetParser/Triple.h"
 #include "llvm/Transforms/Utils/SplitModule.h"
 
 using namespace llvm;
@@ -47,12 +53,45 @@ static cl::opt<bool>
                    cl::desc("Split without externalizing locals"),
                    cl::cat(SplitCategory));
 
+static cl::opt<std::string>
+    MTarget("mtarget",
+            cl::desc("Target triple. When present, a TargetMachine is created "
+                     "and TargetMachine::splitModule is used instead of the "
+                     "common SplitModule logic."),
+            cl::value_desc("triple"), cl::cat(SplitCategory));
+
+static cl::opt<std::string>
+    MCPU("mcpu", cl::desc("Target CPU, ignored if -mtarget is not used"),
+         cl::value_desc("cpu"), cl::cat(SplitCategory));
+
 int main(int argc, char **argv) {
+  InitLLVM X(argc, argv);
+
+  // NOTE: If mtarget is not present we could avoid initializing targets to save
+  // time, but this is a testing tool and it's likely not worth the added
+  // complexity.
+  InitializeAllTargets();
+  InitializeAllTargetMCs();
+
   LLVMContext Context;
   SMDiagnostic Err;
   cl::HideUnrelatedOptions({&SplitCategory, &getColorCategory()});
   cl::ParseCommandLineOptions(argc, argv, "LLVM module splitter\n");
 
+  TargetMachine *TM = nullptr;
+  if (!MTarget.empty()) {
+    std::string Error;
+    const Target *T = TargetRegistry::lookupTarget(MTarget, Error);
+    if (!T) {
+      errs() << "unknown target '" << MTarget << "': " << Error << "\n";
+      return 1;
+    }
+
+    TargetOptions Options;
+    TM = T->createTargetMachine(MTarget, MCPU, /*FS*/ "", Options, std::nullopt,
+                                std::nullopt);
+  }
+
   std::unique_ptr<Module> M = parseIRFile(InputFilename, Err, Context);
 
   if (!M) {
@@ -61,28 +100,40 @@ int main(int argc, char **argv) {
   }
 
   unsigned I = 0;
-  SplitModule(
-      *M, NumOutputs,
-      [&](std::unique_ptr<Module> MPart) {
-        std::error_code EC;
-        std::unique_ptr<ToolOutputFile> Out(new ToolOutputFile(
-            OutputFilename + utostr(I++), EC, sys::fs::OF_None));
-        if (EC) {
-          errs() << EC.message() << '\n';
-          exit(1);
-        }
-
-        if (verifyModule(*MPart, &errs())) {
-          errs() << "Broken module!\n";
-          exit(1);
-        }
-
-        WriteBitcodeToFile(*MPart, Out->os());
-
-        // Declare success.
-        Out->keep();
-      },
-      PreserveLocals);
+  const auto HandleModulePart = [&](std::unique_ptr<Module> MPart) {
+    std::error_code EC;
+    std::unique_ptr<ToolOutputFile> Out(
+        new ToolOutputFile(OutputFilename + utostr(I++), EC, sys::fs::OF_None));
+    if (EC) {
+      errs() << EC.message() << '\n';
+      exit(1);
+    }
+
+    if (verifyModule(*MPart, &errs())) {
+      errs() << "Broken module!\n";
+      exit(1);
+    }
+
+    WriteBitcodeToFile(*MPart, Out->os());
+
+    // Declare success.
+    Out->keep();
+  };
+
+  if (TM) {
+    if (PreserveLocals) {
+      errs() << "warning: -preserve-locals has no effect when using "
+                "TargetMachine::splitModule\n";
+    }
+
+    if (TM->splitModule(*M, NumOutputs, HandleModulePart))
+      return 0;
+
+    errs() << "warning:"
+              "TargetMachine::splitModule failed, falling back to default "
+              "splitModule implementation\n";
+  }
 
+  SplitModule(*M, NumOutputs, HandleModulePart, PreserveLocals);
   return 0;
 }

>From 629a585b00fb01c2b3da66ec7bfa405833f1d605 Mon Sep 17 00:00:00 2001
From: pvanhout <pierre.vanhoutryve at amd.com>
Date: Fri, 19 Apr 2024 10:43:55 +0200
Subject: [PATCH 2/2] comments

---
 .../tools/llvm-split/target-specific-split.ll | 10 ++++++++
 llvm/tools/llvm-split/llvm-split.cpp          | 23 ++++++++-----------
 2 files changed, 20 insertions(+), 13 deletions(-)
 create mode 100644 llvm/test/tools/llvm-split/target-specific-split.ll

diff --git a/llvm/test/tools/llvm-split/target-specific-split.ll b/llvm/test/tools/llvm-split/target-specific-split.ll
new file mode 100644
index 00000000000000..ea4d38084d759a
--- /dev/null
+++ b/llvm/test/tools/llvm-split/target-specific-split.ll
@@ -0,0 +1,10 @@
+; RUN: llvm-split -o %t %s -mtriple x86_64 -preserve-locals 2>&1 | FileCheck %s
+
+; Basic test for a target that doesn't support target-specific module splitting.
+
+; CHECK: warning: -preserve-locals has no effect when using TargetMachine::splitModule
+; CHECK: warning: TargetMachine::splitModule failed, falling back to default splitModule implementation
+
+define void @bar() {
+  ret void
+}
diff --git a/llvm/tools/llvm-split/llvm-split.cpp b/llvm/tools/llvm-split/llvm-split.cpp
index 252e04e77941f2..41f392bcc53117 100644
--- a/llvm/tools/llvm-split/llvm-split.cpp
+++ b/llvm/tools/llvm-split/llvm-split.cpp
@@ -54,41 +54,38 @@ static cl::opt<bool>
                    cl::cat(SplitCategory));
 
 static cl::opt<std::string>
-    MTarget("mtarget",
+    MTriple("mtriple",
             cl::desc("Target triple. When present, a TargetMachine is created "
                      "and TargetMachine::splitModule is used instead of the "
                      "common SplitModule logic."),
             cl::value_desc("triple"), cl::cat(SplitCategory));
 
 static cl::opt<std::string>
-    MCPU("mcpu", cl::desc("Target CPU, ignored if -mtarget is not used"),
+    MCPU("mcpu", cl::desc("Target CPU, ignored if -mtriple is not used"),
          cl::value_desc("cpu"), cl::cat(SplitCategory));
 
 int main(int argc, char **argv) {
   InitLLVM X(argc, argv);
 
-  // NOTE: If mtarget is not present we could avoid initializing targets to save
-  // time, but this is a testing tool and it's likely not worth the added
-  // complexity.
-  InitializeAllTargets();
-  InitializeAllTargetMCs();
-
   LLVMContext Context;
   SMDiagnostic Err;
   cl::HideUnrelatedOptions({&SplitCategory, &getColorCategory()});
   cl::ParseCommandLineOptions(argc, argv, "LLVM module splitter\n");
 
   TargetMachine *TM = nullptr;
-  if (!MTarget.empty()) {
+  if (!MTriple.empty()) {
+    InitializeAllTargets();
+    InitializeAllTargetMCs();
+
     std::string Error;
-    const Target *T = TargetRegistry::lookupTarget(MTarget, Error);
+    const Target *T = TargetRegistry::lookupTarget(MTriple, Error);
     if (!T) {
-      errs() << "unknown target '" << MTarget << "': " << Error << "\n";
+      errs() << "unknown target '" << MTriple << "': " << Error << "\n";
       return 1;
     }
 
     TargetOptions Options;
-    TM = T->createTargetMachine(MTarget, MCPU, /*FS*/ "", Options, std::nullopt,
+    TM = T->createTargetMachine(MTriple, MCPU, /*FS*/ "", Options, std::nullopt,
                                 std::nullopt);
   }
 
@@ -129,7 +126,7 @@ int main(int argc, char **argv) {
     if (TM->splitModule(*M, NumOutputs, HandleModulePart))
       return 0;
 
-    errs() << "warning:"
+    errs() << "warning: "
               "TargetMachine::splitModule failed, falling back to default "
               "splitModule implementation\n";
   }



More information about the llvm-commits mailing list