[Mlir-commits] [mlir] [mlir][gpu] Add an option to represent LLVM bitcode linking flags. (PR #78050)

Fabian Mora llvmlistbot at llvm.org
Sat Jan 13 08:56:11 PST 2024


https://github.com/fabianmcg created https://github.com/llvm/llvm-project/pull/78050

This patch adds an option to `Target/LLVM/ModuleToObject` for specifying the LLVM bitcode linking flags.

One example where this is needed is when compiling OpenMP target offload code, as all the symbols from `libomptarget*.bc` must be imported regardless of usage, because `llvm/Transforms/IPO/OpenMPOpt.cpp` might materialize their use during optimizations.

This patch also adds the `lflags` option to `gpu-module-to-binary` for specifying this linking flags.

>From 8a509c8b7f419d0d870ebdc6423cc66ec5d4c2e3 Mon Sep 17 00:00:00 2001
From: Fabian Mora <fmora.dev at gmail.com>
Date: Sat, 13 Jan 2024 16:44:14 +0000
Subject: [PATCH] [mlir][gpu] Add an option to represent LLVM bitcode linking
 flags.

This patch adds an option to `Target/LLVM/ModuleToObject` for specifying the
LLVM bitcode linking flags.

One example where this is needed is when compiling OpenMP target offload code,
as all the symbols from `libomptarget*.bc` must be imported regardless of usage,
because `llvm/Transforms/IPO/OpenMPOpt.cpp` might materialize their use during
optimizations.

This patch also adds the `lflags` option to `gpu-module-to-binary` for
specifying this linking flags.
---
 .../Dialect/GPU/IR/CompilationInterfaces.h    | 12 ++++++
 .../mlir/Dialect/GPU/Transforms/Passes.td     |  4 +-
 .../include/mlir/Target/LLVM/ModuleToObject.h |  7 +++-
 mlir/include/mlir/Target/LLVM/Options.h       | 39 +++++++++++++++++++
 mlir/lib/Dialect/GPU/IR/GPUDialect.cpp        | 15 ++++++-
 .../Dialect/GPU/Transforms/ModuleToBinary.cpp | 14 ++++++-
 mlir/lib/Target/LLVM/ModuleToObject.cpp       | 21 ++++++----
 mlir/lib/Target/LLVM/NVVM/Target.cpp          |  3 +-
 mlir/lib/Target/LLVM/ROCDL/Target.cpp         |  3 +-
 9 files changed, 105 insertions(+), 13 deletions(-)
 create mode 100644 mlir/include/mlir/Target/LLVM/Options.h

diff --git a/mlir/include/mlir/Dialect/GPU/IR/CompilationInterfaces.h b/mlir/include/mlir/Dialect/GPU/IR/CompilationInterfaces.h
index 6d7cb5ca7a7f81f..db7e1a7b755896c 100644
--- a/mlir/include/mlir/Dialect/GPU/IR/CompilationInterfaces.h
+++ b/mlir/include/mlir/Dialect/GPU/IR/CompilationInterfaces.h
@@ -23,6 +23,7 @@ namespace mlir {
 class SymbolTable;
 namespace LLVM {
 class ModuleTranslation;
+enum class LinkingFlags : uint32_t;
 }
 namespace gpu {
 enum class CompilationTarget : uint32_t;
@@ -52,6 +53,7 @@ class TargetOptions {
       StringRef toolkitPath = {}, ArrayRef<std::string> linkFiles = {},
       StringRef cmdOptions = {},
       CompilationTarget compilationTarget = getDefaultCompilationTarget(),
+      LLVM::LinkingFlags llvmLinkingFlags = getDefaultLinkingFlags(),
       function_ref<SymbolTable *()> getSymbolTableCallback = {});
 
   /// Returns the typeID.
@@ -73,6 +75,9 @@ class TargetOptions {
   /// Returns the compilation target.
   CompilationTarget getCompilationTarget() const;
 
+  /// Returns the LLVM linking flags.
+  LLVM::LinkingFlags getLinkingFlags() const;
+
   /// Returns the result of the `getSymbolTableCallback` callback or a nullptr
   /// if no callback was provided.
   /// Note: The callback itself can return nullptr. It is up to the target how
@@ -83,6 +88,9 @@ class TargetOptions {
   /// Returns the default compilation target: `CompilationTarget::Fatbin`.
   static CompilationTarget getDefaultCompilationTarget();
 
+  /// Returns the default LLVM linking flags: `LLVM::LinkingFlags::onlyNeeded`.
+  static LLVM::LinkingFlags getDefaultLinkingFlags();
+
 protected:
   /// Derived classes must use this constructor to initialize `typeID` to the
   /// appropiate value: ie. `TargetOptions(TypeID::get<DerivedClass>())`.
@@ -90,6 +98,7 @@ class TargetOptions {
       TypeID typeID, StringRef toolkitPath = {},
       ArrayRef<std::string> linkFiles = {}, StringRef cmdOptions = {},
       CompilationTarget compilationTarget = getDefaultCompilationTarget(),
+      LLVM::LinkingFlags llvmLinkingFlags = getDefaultLinkingFlags(),
       function_ref<SymbolTable *()> getSymbolTableCallback = {});
 
   /// Path to the target toolkit.
@@ -105,6 +114,9 @@ class TargetOptions {
   /// Compilation process target format.
   CompilationTarget compilationTarget;
 
+  /// LLVM bitcode linker flags.
+  LLVM::LinkingFlags llvmLinkingFlags;
+
   /// Callback for obtaining the parent symbol table of all the GPU modules
   /// being serialized.
   function_ref<SymbolTable *()> getSymbolTableCallback;
diff --git a/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td b/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
index 3e0f6a3022f935f..601f0fec49a0a31 100644
--- a/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/GPU/Transforms/Passes.td
@@ -97,7 +97,9 @@ def GpuModuleToBinaryPass
     Option<"cmdOptions", "opts", "std::string", [{""}],
            "Command line options to pass to the tools.">,
     Option<"compilationTarget", "format", "std::string", [{"fatbin"}],
-           "The target representation of the compilation process.">
+           "The target representation of the compilation process.">,
+    Option<"linkingFlags", "lflags", "std::string", [{"only-needed"}],
+           "The LLVM bitcode linking flags.">
   ];
 }
 
diff --git a/mlir/include/mlir/Target/LLVM/ModuleToObject.h b/mlir/include/mlir/Target/LLVM/ModuleToObject.h
index e40d7e9a43dd6b5..8b468aafdfac409 100644
--- a/mlir/include/mlir/Target/LLVM/ModuleToObject.h
+++ b/mlir/include/mlir/Target/LLVM/ModuleToObject.h
@@ -15,6 +15,7 @@
 #define MLIR_TARGET_LLVM_MODULETOOBJECT_H
 
 #include "mlir/IR/Operation.h"
+#include "mlir/Target/LLVM/Options.h"
 #include "llvm/IR/Module.h"
 
 namespace llvm {
@@ -30,7 +31,8 @@ class ModuleTranslation;
 class ModuleToObject {
 public:
   ModuleToObject(Operation &module, StringRef triple, StringRef chip,
-                 StringRef features = {}, int optLevel = 3);
+                 StringRef features = {}, int optLevel = 3,
+                 LinkingFlags linkingFlags = LinkingFlags::onlyNeeded);
   virtual ~ModuleToObject();
 
   /// Returns the operation being serialized.
@@ -114,6 +116,9 @@ class ModuleToObject {
   /// Optimization level.
   int optLevel;
 
+  /// Linker flags.
+  LinkingFlags linkingFlags;
+
 private:
   /// The TargetMachine created for the given Triple, if available.
   /// Accessible through `getOrCreateTargetMachine()`.
diff --git a/mlir/include/mlir/Target/LLVM/Options.h b/mlir/include/mlir/Target/LLVM/Options.h
new file mode 100644
index 000000000000000..a370fca0f08c004
--- /dev/null
+++ b/mlir/include/mlir/Target/LLVM/Options.h
@@ -0,0 +1,39 @@
+//===- Options.h - LLVM Target Options --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares LLVM target options.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TARGET_LLVM_OPTIONS_H
+#define MLIR_TARGET_LLVM_OPTIONS_H
+
+#include <cstdint>
+
+namespace mlir {
+namespace LLVM {
+/// This enumeration represents LLVM bitcode linking flags.
+enum class LinkingFlags : uint32_t {
+  none = 0,
+  overrideFromSrc = 1, /// Override symbols using the source definition.
+  onlyNeeded = 2, /// Add only symbols referenced by the destination module.
+};
+
+/// LinkingFlags bitwise operators.
+inline LinkingFlags operator|(LinkingFlags x, LinkingFlags y) {
+  return static_cast<LinkingFlags>(static_cast<uint32_t>(x) |
+                                   static_cast<uint32_t>(y));
+}
+inline LinkingFlags operator&(LinkingFlags x, LinkingFlags y) {
+  return static_cast<LinkingFlags>(static_cast<uint32_t>(x) &
+                                   static_cast<uint32_t>(y));
+}
+} // namespace LLVM
+} // namespace mlir
+
+#endif // MLIR_TARGET_LLVM_OPTIONS_H
diff --git a/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp b/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp
index 020900934c9f726..45ac56cf2fca04c 100644
--- a/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp
+++ b/mlir/lib/Dialect/GPU/IR/GPUDialect.cpp
@@ -29,6 +29,7 @@
 #include "mlir/Interfaces/FunctionImplementation.h"
 #include "mlir/Interfaces/SideEffectInterfaces.h"
 #include "mlir/Support/LogicalResult.h"
+#include "mlir/Target/LLVM/Options.h"
 #include "mlir/Transforms/InliningUtils.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/TypeSwitch.h"
@@ -2211,16 +2212,20 @@ LogicalResult gpu::DynamicSharedMemoryOp::verify() {
 TargetOptions::TargetOptions(
     StringRef toolkitPath, ArrayRef<std::string> linkFiles,
     StringRef cmdOptions, CompilationTarget compilationTarget,
+    LLVM::LinkingFlags llvmLinkingFlags,
     function_ref<SymbolTable *()> getSymbolTableCallback)
     : TargetOptions(TypeID::get<TargetOptions>(), toolkitPath, linkFiles,
-                    cmdOptions, compilationTarget, getSymbolTableCallback) {}
+                    cmdOptions, compilationTarget, llvmLinkingFlags,
+                    getSymbolTableCallback) {}
 
 TargetOptions::TargetOptions(
     TypeID typeID, StringRef toolkitPath, ArrayRef<std::string> linkFiles,
     StringRef cmdOptions, CompilationTarget compilationTarget,
+    LLVM::LinkingFlags llvmLinkingFlags,
     function_ref<SymbolTable *()> getSymbolTableCallback)
     : toolkitPath(toolkitPath.str()), linkFiles(linkFiles),
       cmdOptions(cmdOptions.str()), compilationTarget(compilationTarget),
+      llvmLinkingFlags(llvmLinkingFlags),
       getSymbolTableCallback(getSymbolTableCallback), typeID(typeID) {}
 
 TypeID TargetOptions::getTypeID() const { return typeID; }
@@ -2243,6 +2248,14 @@ CompilationTarget TargetOptions::getDefaultCompilationTarget() {
   return CompilationTarget::Fatbin;
 }
 
+LLVM::LinkingFlags TargetOptions::getLinkingFlags() const {
+  return llvmLinkingFlags;
+}
+
+LLVM::LinkingFlags TargetOptions::getDefaultLinkingFlags() {
+  return LLVM::LinkingFlags::onlyNeeded;
+}
+
 std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>>
 TargetOptions::tokenizeCmdOptions() const {
   std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>> options;
diff --git a/mlir/lib/Dialect/GPU/Transforms/ModuleToBinary.cpp b/mlir/lib/Dialect/GPU/Transforms/ModuleToBinary.cpp
index 70d36297e103f3f..91d545ce3804887 100644
--- a/mlir/lib/Dialect/GPU/Transforms/ModuleToBinary.cpp
+++ b/mlir/lib/Dialect/GPU/Transforms/ModuleToBinary.cpp
@@ -20,6 +20,7 @@
 #include "mlir/Dialect/LLVMIR/ROCDLDialect.h"
 #include "mlir/Dialect/SPIRV/IR/SPIRVDialect.h"
 #include "mlir/IR/BuiltinOps.h"
+#include "mlir/Target/LLVM/Options.h"
 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
 
 #include "llvm/ADT/STLExtras.h"
@@ -69,6 +70,17 @@ void GpuModuleToBinaryPass::runOnOperation() {
   if (!targetFormat)
     getOperation()->emitError() << "Invalid format specified.";
 
+  auto lFlags =
+      llvm::StringSwitch<std::optional<LLVM::LinkingFlags>>(linkingFlags)
+          .Case("only-needed", LLVM::LinkingFlags::onlyNeeded)
+          .Case("override", LLVM::LinkingFlags::overrideFromSrc)
+          .Cases("none", "", LLVM::LinkingFlags::none)
+          .Case("override-only-needed", LLVM::LinkingFlags::overrideFromSrc |
+                                            LLVM::LinkingFlags::onlyNeeded)
+          .Default(std::nullopt);
+  if (!lFlags)
+    getOperation()->emitError() << "Invalid linking options specified.";
+
   // Lazy symbol table builder callback.
   std::optional<SymbolTable> parentTable;
   auto lazyTableBuilder = [&]() -> SymbolTable * {
@@ -85,7 +97,7 @@ void GpuModuleToBinaryPass::runOnOperation() {
   };
 
   TargetOptions targetOptions(toolkitPath, linkFiles, cmdOptions, *targetFormat,
-                              lazyTableBuilder);
+                              *lFlags, lazyTableBuilder);
   if (failed(transformGpuModulesToBinaries(
           getOperation(),
           offloadingHandler ? dyn_cast<OffloadingLLVMTranslationAttrInterface>(
diff --git a/mlir/lib/Target/LLVM/ModuleToObject.cpp b/mlir/lib/Target/LLVM/ModuleToObject.cpp
index d94c10de8d7c424..258656173c72394 100644
--- a/mlir/lib/Target/LLVM/ModuleToObject.cpp
+++ b/mlir/lib/Target/LLVM/ModuleToObject.cpp
@@ -35,9 +35,10 @@ using namespace mlir;
 using namespace mlir::LLVM;
 
 ModuleToObject::ModuleToObject(Operation &module, StringRef triple,
-                               StringRef chip, StringRef features, int optLevel)
+                               StringRef chip, StringRef features, int optLevel,
+                               LinkingFlags linkingFlags)
     : module(module), triple(triple), chip(chip), features(features),
-      optLevel(optLevel) {}
+      optLevel(optLevel), linkingFlags(linkingFlags) {}
 
 ModuleToObject::~ModuleToObject() = default;
 
@@ -107,6 +108,15 @@ ModuleToObject::translateToLLVMIR(llvm::LLVMContext &llvmContext) {
   return translateModuleToLLVMIR(&getOperation(), llvmContext);
 }
 
+static unsigned convertFlags(LinkingFlags flags) {
+  unsigned res = llvm::Linker::Flags::None;
+  if ((flags & LinkingFlags::onlyNeeded) == LinkingFlags::onlyNeeded)
+    res |= llvm::Linker::Flags::LinkOnlyNeeded;
+  if ((flags & LinkingFlags::overrideFromSrc) == LinkingFlags::overrideFromSrc)
+    res |= llvm::Linker::Flags::OverrideFromSrc;
+  return res;
+}
+
 LogicalResult
 ModuleToObject::linkFiles(llvm::Module &module,
                           SmallVector<std::unique_ptr<llvm::Module>> &&libs) {
@@ -116,12 +126,9 @@ ModuleToObject::linkFiles(llvm::Module &module,
   for (std::unique_ptr<llvm::Module> &libModule : libs) {
     // This bitcode linking imports the library functions into the module,
     // allowing LLVM optimization passes (which must run after linking) to
-    // optimize across the libraries and the module's code. We also only import
-    // symbols if they are referenced by the module or a previous library since
-    // there will be no other source of references to those symbols in this
-    // compilation and since we don't want to bloat the resulting code object.
+    // optimize across the libraries and the module's code.
     bool err = linker.linkInModule(
-        std::move(libModule), llvm::Linker::Flags::LinkOnlyNeeded,
+        std::move(libModule), convertFlags(linkingFlags),
         [](llvm::Module &m, const StringSet<> &gvs) {
           llvm::internalizeModule(m, [&gvs](const llvm::GlobalValue &gv) {
             return !gv.hasName() || (gvs.count(gv.getName()) == 0);
diff --git a/mlir/lib/Target/LLVM/NVVM/Target.cpp b/mlir/lib/Target/LLVM/NVVM/Target.cpp
index eaf94147e2a6f1f..4782158af102de9 100644
--- a/mlir/lib/Target/LLVM/NVVM/Target.cpp
+++ b/mlir/lib/Target/LLVM/NVVM/Target.cpp
@@ -84,7 +84,8 @@ SerializeGPUModuleBase::SerializeGPUModuleBase(
     Operation &module, NVVMTargetAttr target,
     const gpu::TargetOptions &targetOptions)
     : ModuleToObject(module, target.getTriple(), target.getChip(),
-                     target.getFeatures(), target.getO()),
+                     target.getFeatures(), target.getO(),
+                     targetOptions.getLinkingFlags()),
       target(target), toolkitPath(targetOptions.getToolkitPath()),
       fileList(targetOptions.getLinkFiles()) {
 
diff --git a/mlir/lib/Target/LLVM/ROCDL/Target.cpp b/mlir/lib/Target/LLVM/ROCDL/Target.cpp
index a589e5cbbb61983..94d97242d1023d3 100644
--- a/mlir/lib/Target/LLVM/ROCDL/Target.cpp
+++ b/mlir/lib/Target/LLVM/ROCDL/Target.cpp
@@ -98,7 +98,8 @@ SerializeGPUModuleBase::SerializeGPUModuleBase(
     Operation &module, ROCDLTargetAttr target,
     const gpu::TargetOptions &targetOptions)
     : ModuleToObject(module, target.getTriple(), target.getChip(),
-                     target.getFeatures(), target.getO()),
+                     target.getFeatures(), target.getO(),
+                     targetOptions.getLinkingFlags()),
       target(target), toolkitPath(targetOptions.getToolkitPath()),
       fileList(targetOptions.getLinkFiles()) {
 



More information about the Mlir-commits mailing list