diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index 638c31b39682e..1cb64103865a9 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -1825,4 +1825,33 @@ 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 = [{ + convertLinkerOptionsOp($options, builder, moduleTranslation); + }]; + + let hasVerifier = 1; +} + #endif // LLVMIR_OPS diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h index 9bedc84e0bfa1..5f5adbff6c04e 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/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp index 7f5681e7bdc05..691651199cdfa 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -3072,6 +3072,17 @@ 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 1c0f51a66bf5e..1be58a39b40d2 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp @@ -172,6 +172,24 @@ 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 MDNodes; + MDNodes.reserve(options.size()); + for (auto s : options.getAsRange()) { + auto *MDNode = llvm::MDString::get(context, s.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/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp index e3562049cd81c..ec1123658d70a 100644 --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -487,6 +487,23 @@ 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 options; + options.reserve(md->getNumOperands()); + for (const llvm::MDOperand &option : md->operands()) + options.push_back(cast(option)->getString()); + builder.create(mlirModule.getLoc(), + builder.getStrArrayAttr(options)); + } + } + return success(); +} + LogicalResult ModuleImport::convertMetadata() { OpBuilder::InsertionGuard guard(builder); builder.setInsertionPointToEnd(mlirModule.getBody()); @@ -513,6 +530,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 0000000000000..8702415c29886 --- /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-invalid.mlir b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir index 2d6ccff2d436f..9f87358306744 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"] +} diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir index 7da44b6fbe1ab..8b88016595eff 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"]