Skip to content

Commit a72e034

Browse files
authored
[mlir] Add llvm.linker.options operation to the LLVM IR Dialect (#71720)
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.
1 parent 002da67 commit a72e034

File tree

8 files changed

+109
-0
lines changed

8 files changed

+109
-0
lines changed

mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1797,4 +1797,33 @@ def LLVM_CallIntrinsicOp
17971797
let hasVerifier = 1;
17981798
}
17991799

1800+
def LLVM_LinkerOptionsOp
1801+
: LLVM_Op<"linker_options"> {
1802+
let summary = "Options to pass to the linker when the object file is linked";
1803+
let description = [{
1804+
Pass the given options to the linker when the resulting object file is linked.
1805+
This is used extensively on Windows to determine the C runtime that the object
1806+
files should link against.
1807+
1808+
Examples:
1809+
```mlir
1810+
// Link against the MSVC static threaded CRT.
1811+
llvm.linker_options ["/DEFAULTLIB:", "libcmt"]
1812+
1813+
// Link against aarch64 compiler-rt builtins
1814+
llvm.linker_options ["-l", "clang_rt.builtins-aarch64"]
1815+
```
1816+
}];
1817+
let arguments = (ins StrArrayAttr:$options);
1818+
let assemblyFormat = [{
1819+
$options attr-dict
1820+
}];
1821+
1822+
let llvmBuilder = [{
1823+
convertLinkerOptionsOp($options, builder, moduleTranslation);
1824+
}];
1825+
1826+
let hasVerifier = 1;
1827+
}
1828+
18001829
#endif // LLVMIR_OPS

mlir/include/mlir/Target/LLVMIR/ModuleImport.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ class ModuleImport {
177177
/// implement the fastmath interface.
178178
void setFastmathFlagsAttr(llvm::Instruction *inst, Operation *op) const;
179179

180+
/// Converts !llvm.linker.options metadata to the llvm.linker.options
181+
/// LLVM dialect operation.
182+
LogicalResult convertLinkerOptionsMetadata();
183+
180184
/// Converts all LLVM metadata nodes that translate to attributes such as
181185
/// alias analysis or access group metadata, and builds a map from the
182186
/// metadata nodes to the converted attributes.

mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2912,6 +2912,17 @@ struct LLVMOpAsmDialectInterface : public OpAsmDialectInterface {
29122912
};
29132913
} // namespace
29142914

2915+
//===----------------------------------------------------------------------===//
2916+
// LinkerOptionsOp
2917+
//===----------------------------------------------------------------------===//
2918+
2919+
LogicalResult LinkerOptionsOp::verify() {
2920+
if (mlir::Operation *parentOp = (*this)->getParentOp();
2921+
parentOp && !satisfiesLLVMModule(parentOp))
2922+
return emitOpError("must appear at the module level");
2923+
return success();
2924+
}
2925+
29152926
//===----------------------------------------------------------------------===//
29162927
// LLVMDialect initialization, type parsing, and registration.
29172928
//===----------------------------------------------------------------------===//

mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,24 @@ convertCallLLVMIntrinsicOp(CallIntrinsicOp op, llvm::IRBuilderBase &builder,
172172
return success();
173173
}
174174

175+
static void convertLinkerOptionsOp(ArrayAttr options,
176+
llvm::IRBuilderBase &builder,
177+
LLVM::ModuleTranslation &moduleTranslation) {
178+
llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
179+
llvm::LLVMContext &context = llvmModule->getContext();
180+
llvm::NamedMDNode *linkerMDNode =
181+
llvmModule->getOrInsertNamedMetadata("llvm.linker.options");
182+
SmallVector<llvm::Metadata *> MDNodes;
183+
MDNodes.reserve(options.size());
184+
for (auto s : options.getAsRange<StringAttr>()) {
185+
auto *MDNode = llvm::MDString::get(context, s.getValue());
186+
MDNodes.push_back(MDNode);
187+
}
188+
189+
auto *listMDNode = llvm::MDTuple::get(context, MDNodes);
190+
linkerMDNode->addOperand(listMDNode);
191+
}
192+
175193
static LogicalResult
176194
convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
177195
LLVM::ModuleTranslation &moduleTranslation) {

mlir/lib/Target/LLVMIR/ModuleImport.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,23 @@ void ModuleImport::addDebugIntrinsic(llvm::CallInst *intrinsic) {
487487
debugIntrinsics.insert(intrinsic);
488488
}
489489

490+
LogicalResult ModuleImport::convertLinkerOptionsMetadata() {
491+
for (const llvm::NamedMDNode &named : llvmModule->named_metadata()) {
492+
if (named.getName() != "llvm.linker.options")
493+
continue;
494+
// llvm.linker.options operands are lists of strings.
495+
for (const llvm::MDNode *md : named.operands()) {
496+
SmallVector<StringRef> options;
497+
options.reserve(md->getNumOperands());
498+
for (const llvm::MDOperand &option : md->operands())
499+
options.push_back(cast<llvm::MDString>(option)->getString());
500+
builder.create<LLVM::LinkerOptionsOp>(mlirModule.getLoc(),
501+
builder.getStrArrayAttr(options));
502+
}
503+
}
504+
return success();
505+
}
506+
490507
LogicalResult ModuleImport::convertMetadata() {
491508
OpBuilder::InsertionGuard guard(builder);
492509
builder.setInsertionPointToEnd(mlirModule.getBody());
@@ -513,6 +530,8 @@ LogicalResult ModuleImport::convertMetadata() {
513530
return failure();
514531
}
515532
}
533+
if (failed(convertLinkerOptionsMetadata()))
534+
return failure();
516535
return success();
517536
}
518537

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
; RUN: mlir-translate -import-llvm -split-input-file %s | FileCheck %s
2+
3+
; CHECK: llvm.linker_options ["DEFAULTLIB:", "libcmt"]
4+
!llvm.linker.options = !{!0}
5+
!0 = !{!"DEFAULTLIB:", !"libcmt"}
6+
7+
; // -----
8+
9+
!llvm.linker.options = !{!0, !1, !2}
10+
; CHECK: llvm.linker_options ["DEFAULTLIB:", "libcmt"]
11+
!0 = !{!"DEFAULTLIB:", !"libcmt"}
12+
; CHECK: llvm.linker_options ["DEFAULTLIB:", "libcmtd"]
13+
!1 = !{!"DEFAULTLIB:", !"libcmtd"}
14+
; CHECK: llvm.linker_options ["-lm"]
15+
!2 = !{!"-lm"}

mlir/test/Target/LLVMIR/llvmir-invalid.mlir

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,10 @@ llvm.comdat @__llvm_comdat_1 {
253253
// expected-error @below{{comdat selection symbols must be unique even in different comdat regions}}
254254
llvm.comdat_selector @foo any
255255
}
256+
257+
// -----
258+
259+
llvm.func @foo() {
260+
// expected-error @below{{must appear at the module level}}
261+
llvm.linker_options ["test"]
262+
}

mlir/test/Target/LLVMIR/llvmir.mlir

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2324,3 +2324,9 @@ llvm.func @zeroinit_complex_local_aggregate() {
23242324

23252325
llvm.return
23262326
}
2327+
2328+
//CHECK: !llvm.linker.options = !{![[MD0:[0-9]+]], ![[MD1:[0-9]+]]}
2329+
//CHECK: ![[MD0]] = !{!"/DEFAULTLIB:", !"libcmt"}
2330+
llvm.linker_options ["/DEFAULTLIB:", "libcmt"]
2331+
//CHECK: ![[MD1]] = !{!"/DEFAULTLIB:", !"libcmtd"}
2332+
llvm.linker_options ["/DEFAULTLIB:", "libcmtd"]

0 commit comments

Comments
 (0)