[Mlir-commits] [mlir] [mlir] Add llvm.linker.options operation to the LLVM IR Dialect (PR #71720)

David Truby llvmlistbot at llvm.org
Thu Nov 9 07:58:10 PST 2023


https://github.com/DavidTruby updated https://github.com/llvm/llvm-project/pull/71720

>From 9d0fe8fb6b0c2dab436ad2fe3ced7504d19b5186 Mon Sep 17 00:00:00 2001
From: David Truby <david at truby.dev>
Date: Wed, 8 Nov 2023 18:35:23 +0000
Subject: [PATCH 1/3] [mlir] Add llvm.linker.options operation to the LLVM IR
 Dialect

This patch adds a `llvm.linker.options` operation taking a list of
strings to pass to the linker when the resulting object file is linked.
This is particularly useful on Windows to specify the CRT version to use
for this object file.
---
 mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td   | 38 +++++++++++++++++++
 .../include/mlir/Target/LLVMIR/ModuleImport.h |  4 ++
 mlir/lib/Target/LLVMIR/ModuleImport.cpp       | 23 +++++++++++
 .../LLVMIR/Import/metadata-linker-options.ll  | 15 ++++++++
 mlir/test/Target/LLVMIR/llvmir.mlir           |  6 +++
 5 files changed, 86 insertions(+)
 create mode 100644 mlir/test/Target/LLVMIR/Import/metadata-linker-options.ll

diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 638c31b39682ea6..7635bc92be0e136 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1825,4 +1825,42 @@ def LLVM_CallIntrinsicOp
   let hasVerifier = 1;
 }
 
+def LLVM_LinkerOptionsOp
+    : LLVM_Op<"linker.options"> {
+  let summary = "Options to pass to the linker when the object file is linked";
+  let description = [{
+    Pass the given options to the linker when the resulting object file is linked.
+    This is used extensively on Windows to determine the C runtime that the object
+    files should link against.
+
+    Examples:
+    ```mlir
+    // Link against the MSVC static threaded CRT.
+    llvm.linker.options ["/DEFAULTLIB:", "libcmt"]
+
+    // Link against aarch64 compiler-rt builtins
+    llvm.linker.options ["-l", "clang_rt.builtins-aarch64"]
+    ```
+  }];
+  let arguments  = (ins StrArrayAttr:$options);
+  let assemblyFormat = [{
+    $options attr-dict
+  }];
+
+  let llvmBuilder = [{
+    llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
+    llvm::LLVMContext &context = llvmModule->getContext();
+    llvm::NamedMDNode *linkerMDNode = llvmModule->getOrInsertNamedMetadata("llvm.linker.options");
+    SmallVector<llvm::Metadata*> MDNodes;
+    for (auto s : $options) {
+      auto str = cast<StringAttr>(s);
+      auto *MDNode = llvm::MDString::get(context, str.getValue());
+      MDNodes.push_back(MDNode);
+    }
+
+    auto *listMDNode = llvm::MDTuple::get(context, MDNodes);
+    linkerMDNode->addOperand(listMDNode);
+  }];
+}
+
 #endif // LLVMIR_OPS
diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h
index 9bedc84e0bfa169..5f5adbff6c04ef8 100644
--- a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h
+++ b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h
@@ -177,6 +177,10 @@ class ModuleImport {
   /// implement the fastmath interface.
   void setFastmathFlagsAttr(llvm::Instruction *inst, Operation *op) const;
 
+  /// Converts !llvm.linker.options metadata to the llvm.linker.options
+  /// LLVM dialect operation.
+  LogicalResult convertLinkerOptionsMetadata();
+
   /// Converts all LLVM metadata nodes that translate to attributes such as
   /// alias analysis or access group metadata, and builds a map from the
   /// metadata nodes to the converted attributes.
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index e3562049cd81c76..7d133b309f87a7c 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -487,6 +487,27 @@ void ModuleImport::addDebugIntrinsic(llvm::CallInst *intrinsic) {
   debugIntrinsics.insert(intrinsic);
 }
 
+LogicalResult ModuleImport::convertLinkerOptionsMetadata() {
+  for (const llvm::NamedMDNode &named : llvmModule->named_metadata()) {
+    if (named.getName() != "llvm.linker.options")
+      continue;
+    // llvm.linker.options operands are lists of strings.
+    for (const llvm::MDNode *md : named.operands()) {
+      SmallVector<StringRef> options;
+      for (const llvm::MDOperand &option : md->operands()) {
+        if (auto str = dyn_cast_or_null<llvm::MDString>(option))
+          options.push_back(str->getString());
+        else
+          return emitError(mlirModule.getLoc(),
+                           "argument to llvm.linker.options is not a string");
+      }
+      builder.create<LLVM::LinkerOptionsOp>(mlirModule.getLoc(),
+                                            builder.getStrArrayAttr(options));
+    }
+  }
+  return success();
+}
+
 LogicalResult ModuleImport::convertMetadata() {
   OpBuilder::InsertionGuard guard(builder);
   builder.setInsertionPointToEnd(mlirModule.getBody());
@@ -513,6 +534,8 @@ LogicalResult ModuleImport::convertMetadata() {
           return failure();
     }
   }
+  if (failed(convertLinkerOptionsMetadata()))
+    return failure();
   return success();
 }
 
diff --git a/mlir/test/Target/LLVMIR/Import/metadata-linker-options.ll b/mlir/test/Target/LLVMIR/Import/metadata-linker-options.ll
new file mode 100644
index 000000000000000..473d26ecd598959
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/Import/metadata-linker-options.ll
@@ -0,0 +1,15 @@
+; RUN: mlir-translate -import-llvm -split-input-file %s | FileCheck %s
+
+; CHECK: llvm.linker.options ["DEFAULTLIB:", "libcmt"]
+!llvm.linker.options = !{!0}
+!0 = !{!"DEFAULTLIB:", !"libcmt"}
+
+; // -----
+
+!llvm.linker.options = !{!0, !1, !2}
+; CHECK: llvm.linker.options ["DEFAULTLIB:", "libcmt"]
+!0 = !{!"DEFAULTLIB:", !"libcmt"}
+; CHECK: llvm.linker.options ["DEFAULTLIB:", "libcmtd"]
+!1 = !{!"DEFAULTLIB:", !"libcmtd"}
+; CHECK: llvm.linker.options ["-lm"]
+!2 = !{!"-lm"}
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 7da44b6fbe1ab33..fa3cd7679112440 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -2337,3 +2337,9 @@ llvm.func @zeroinit_complex_local_aggregate() {
 
   llvm.return
 }
+
+//CHECK: !llvm.linker.options = !{![[MD0:[0-9]+]], ![[MD1:[0-9]+]]}
+//CHECK: ![[MD0]] = !{!"/DEFAULTLIB:", !"libcmt"}
+llvm.linker.options ["/DEFAULTLIB:", "libcmt"]
+//CHECK: ![[MD1]] = !{!"/DEFAULTLIB:", !"libcmtd"}
+llvm.linker.options ["/DEFAULTLIB:", "libcmtd"]
\ No newline at end of file

>From bf0f72051e7d1edd8b926fb54b611e843c1f83ea Mon Sep 17 00:00:00 2001
From: David Truby <david at truby.dev>
Date: Thu, 9 Nov 2023 15:53:53 +0000
Subject: [PATCH 2/3] Fixes for review

* Change name of operation to linker_options
* Add verifier that the operation can only appear in modules
* Move C++ code into a static function
---
 mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td   | 21 ++++++-------------
 mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp    | 12 +++++++++++
 .../LLVMIR/LLVMToLLVMIRTranslation.cpp        | 16 ++++++++++++++
 .../LLVMIR/Import/metadata-linker-options.ll  |  8 +++----
 mlir/test/Target/LLVMIR/llvmir-invalid.mlir   |  7 +++++++
 mlir/test/Target/LLVMIR/llvmir.mlir           |  4 ++--
 6 files changed, 47 insertions(+), 21 deletions(-)

diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
index 7635bc92be0e136..1cb64103865a9d1 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
@@ -1826,7 +1826,7 @@ def LLVM_CallIntrinsicOp
 }
 
 def LLVM_LinkerOptionsOp
-    : LLVM_Op<"linker.options"> {
+    : LLVM_Op<"linker_options"> {
   let summary = "Options to pass to the linker when the object file is linked";
   let description = [{
     Pass the given options to the linker when the resulting object file is linked.
@@ -1836,10 +1836,10 @@ def LLVM_LinkerOptionsOp
     Examples:
     ```mlir
     // Link against the MSVC static threaded CRT.
-    llvm.linker.options ["/DEFAULTLIB:", "libcmt"]
+    llvm.linker_options ["/DEFAULTLIB:", "libcmt"]
 
     // Link against aarch64 compiler-rt builtins
-    llvm.linker.options ["-l", "clang_rt.builtins-aarch64"]
+    llvm.linker_options ["-l", "clang_rt.builtins-aarch64"]
     ```
   }];
   let arguments  = (ins StrArrayAttr:$options);
@@ -1848,19 +1848,10 @@ def LLVM_LinkerOptionsOp
   }];
 
   let llvmBuilder = [{
-    llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
-    llvm::LLVMContext &context = llvmModule->getContext();
-    llvm::NamedMDNode *linkerMDNode = llvmModule->getOrInsertNamedMetadata("llvm.linker.options");
-    SmallVector<llvm::Metadata*> MDNodes;
-    for (auto s : $options) {
-      auto str = cast<StringAttr>(s);
-      auto *MDNode = llvm::MDString::get(context, str.getValue());
-      MDNodes.push_back(MDNode);
-    }
-
-    auto *listMDNode = llvm::MDTuple::get(context, MDNodes);
-    linkerMDNode->addOperand(listMDNode);
+    convertLinkerOptionsOp($options, builder, moduleTranslation);
   }];
+
+  let hasVerifier = 1;
 }
 
 #endif // LLVMIR_OPS
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
index 7f5681e7bdc0592..27e79ab5d611616 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
@@ -3072,6 +3072,18 @@ struct LLVMOpAsmDialectInterface : public OpAsmDialectInterface {
 };
 } // namespace
 
+
+//===----------------------------------------------------------------------===//
+// LinkerOptionsOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult LinkerOptionsOp::verify() {
+  if (mlir::Operation *parentOp = (*this)->getParentOp();
+      parentOp && !satisfiesLLVMModule(parentOp))
+      return emitOpError("must appear at the module level");
+  return success();
+}
+
 //===----------------------------------------------------------------------===//
 // LLVMDialect initialization, type parsing, and registration.
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index 1c0f51a66bf5e8c..29fc1a19b318a6d 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -172,6 +172,22 @@ convertCallLLVMIntrinsicOp(CallIntrinsicOp op, llvm::IRBuilderBase &builder,
   return success();
 }
 
+static void convertLinkerOptionsOp(ArrayAttr options, llvm::IRBuilderBase &builder,
+                                   LLVM::ModuleTranslation &moduleTranslation) {
+    llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
+    llvm::LLVMContext &context = llvmModule->getContext();
+    llvm::NamedMDNode *linkerMDNode = llvmModule->getOrInsertNamedMetadata("llvm.linker.options");
+    SmallVector<llvm::Metadata*> MDNodes;
+    for (auto s : options) {
+      auto str = cast<StringAttr>(s);
+      auto *MDNode = llvm::MDString::get(context, str.getValue());
+      MDNodes.push_back(MDNode);
+    }
+
+    auto *listMDNode = llvm::MDTuple::get(context, MDNodes);
+    linkerMDNode->addOperand(listMDNode);
+}
+
 static LogicalResult
 convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
                      LLVM::ModuleTranslation &moduleTranslation) {
diff --git a/mlir/test/Target/LLVMIR/Import/metadata-linker-options.ll b/mlir/test/Target/LLVMIR/Import/metadata-linker-options.ll
index 473d26ecd598959..8702415c298862f 100644
--- a/mlir/test/Target/LLVMIR/Import/metadata-linker-options.ll
+++ b/mlir/test/Target/LLVMIR/Import/metadata-linker-options.ll
@@ -1,15 +1,15 @@
 ; RUN: mlir-translate -import-llvm -split-input-file %s | FileCheck %s
 
-; CHECK: llvm.linker.options ["DEFAULTLIB:", "libcmt"]
+; CHECK: llvm.linker_options ["DEFAULTLIB:", "libcmt"]
 !llvm.linker.options = !{!0}
 !0 = !{!"DEFAULTLIB:", !"libcmt"}
 
 ; // -----
 
 !llvm.linker.options = !{!0, !1, !2}
-; CHECK: llvm.linker.options ["DEFAULTLIB:", "libcmt"]
+; CHECK: llvm.linker_options ["DEFAULTLIB:", "libcmt"]
 !0 = !{!"DEFAULTLIB:", !"libcmt"}
-; CHECK: llvm.linker.options ["DEFAULTLIB:", "libcmtd"]
+; CHECK: llvm.linker_options ["DEFAULTLIB:", "libcmtd"]
 !1 = !{!"DEFAULTLIB:", !"libcmtd"}
-; CHECK: llvm.linker.options ["-lm"]
+; CHECK: llvm.linker_options ["-lm"]
 !2 = !{!"-lm"}
diff --git a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
index 2d6ccff2d436fea..776dffa162c5355 100644
--- a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir
@@ -253,3 +253,10 @@ llvm.comdat @__llvm_comdat_1 {
   // expected-error @below{{comdat selection symbols must be unique even in different comdat regions}}
   llvm.comdat_selector @foo any
 }
+
+// -----
+
+llvm.func @foo() {
+  // expected-error @below{{must appear at the module level}}
+  llvm.linker_options ["test"]
+}
\ No newline at end of file
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index fa3cd7679112440..8b88016595effed 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -2340,6 +2340,6 @@ llvm.func @zeroinit_complex_local_aggregate() {
 
 //CHECK: !llvm.linker.options = !{![[MD0:[0-9]+]], ![[MD1:[0-9]+]]}
 //CHECK: ![[MD0]] = !{!"/DEFAULTLIB:", !"libcmt"}
-llvm.linker.options ["/DEFAULTLIB:", "libcmt"]
+llvm.linker_options ["/DEFAULTLIB:", "libcmt"]
 //CHECK: ![[MD1]] = !{!"/DEFAULTLIB:", !"libcmtd"}
-llvm.linker.options ["/DEFAULTLIB:", "libcmtd"]
\ No newline at end of file
+llvm.linker_options ["/DEFAULTLIB:", "libcmtd"]

>From a42a125705291daabf79a2efd77a6ad33b63fce0 Mon Sep 17 00:00:00 2001
From: David Truby <david at truby.dev>
Date: Thu, 9 Nov 2023 15:57:34 +0000
Subject: [PATCH 3/3] Assume verified IR

---
 mlir/lib/Target/LLVMIR/ModuleImport.cpp | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 7d133b309f87a7c..c28143713841ce0 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -495,11 +495,7 @@ LogicalResult ModuleImport::convertLinkerOptionsMetadata() {
     for (const llvm::MDNode *md : named.operands()) {
       SmallVector<StringRef> options;
       for (const llvm::MDOperand &option : md->operands()) {
-        if (auto str = dyn_cast_or_null<llvm::MDString>(option))
-          options.push_back(str->getString());
-        else
-          return emitError(mlirModule.getLoc(),
-                           "argument to llvm.linker.options is not a string");
+        options.push_back(cast<llvm::MDString>(option)->getString());
       }
       builder.create<LLVM::LinkerOptionsOp>(mlirModule.getLoc(),
                                             builder.getStrArrayAttr(options));



More information about the Mlir-commits mailing list