diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td index d90d2ba6d6181..ede9f2d365b20 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td @@ -1291,4 +1291,29 @@ def LLVM_DereferenceableAttr : LLVM_Attr<"Dereferenceable", "dereferenceable"> { let assemblyFormat = "`<` struct(params) `>`"; } +//===----------------------------------------------------------------------===// +// ModuleFlagAttr +//===----------------------------------------------------------------------===// + +def ModuleFlagAttr + : LLVM_Attr<"ModuleFlag", "mlir.module_flag"> { + let summary = "LLVM module flag metadata"; + let description = [{ + Represents a single entry of llvm.module.flags metadata + (llvm::Module::ModuleFlagEntry in LLVM). The first element is a behavior + flag described by `ModFlagBehaviorAttr`, the second is a string ID + and third is the value of the flag (currently only integer constants + are supported). + + Example: + ```mlir + #llvm.mlir.module_flag + ``` + }]; + let parameters = (ins "ModFlagBehavior":$behavior, + "StringAttr":$key, + "uint32_t":$value); + let assemblyFormat = "`<` $behavior `,` $key `,` $value `>`"; +} + #endif // LLVMIR_ATTRDEFS diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td index d35f48b13b2d8..46fae44f7b0fa 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.td @@ -33,6 +33,7 @@ def LLVM_Dialect : Dialect { static StringRef getAliasScopesAttrName() { return "alias_scopes"; } static StringRef getAccessGroupsAttrName() { return "access_groups"; } static StringRef getIdentAttrName() { return "llvm.ident"; } + static StringRef getModuleFlags() { return "llvm.module.flags"; } static StringRef getCommandlineAttrName() { return "llvm.commandline"; } /// Names of llvm parameter attributes. diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td index c08b75de03647..a9de787806452 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMEnums.td @@ -818,4 +818,37 @@ def FPExceptionBehaviorAttr : LLVM_EnumAttr< let cppNamespace = "::mlir::LLVM"; } +//===----------------------------------------------------------------------===// +// Module Flag Behavior +//===----------------------------------------------------------------------===// + +// These values must match llvm::Module::ModFlagBehavior ones. +// See llvm/include/llvm/IR/Module.h. +def ModFlagBehaviorError + : LLVM_EnumAttrCase<"Error", "error", "Error", 1>; +def ModFlagBehaviorWarning + : LLVM_EnumAttrCase<"Warning", "warning", "Warning", 2>; +def ModFlagBehaviorRequire + : LLVM_EnumAttrCase<"Require", "require", "Require", 3>; +def ModFlagBehaviorOverride + : LLVM_EnumAttrCase<"Override", "override", "Override", 4>; +def ModFlagBehaviorAppend + : LLVM_EnumAttrCase<"Append", "append", "Append", 5>; +def ModFlagBehaviorAppendUnique + : LLVM_EnumAttrCase<"AppendUnique", "append_unique", "AppendUnique", 6>; +def ModFlagBehaviorMax + : LLVM_EnumAttrCase<"Max", "max", "Max", 7>; +def ModFlagBehaviorMin + : LLVM_EnumAttrCase<"Min", "min", "Min", 8>; + +def ModFlagBehaviorAttr : LLVM_EnumAttr< + "ModFlagBehavior", + "::llvm::Module::ModFlagBehavior", + "LLVM Module Flag Behavior", + [ModFlagBehaviorError, ModFlagBehaviorWarning, ModFlagBehaviorRequire, + ModFlagBehaviorOverride, ModFlagBehaviorAppend, + ModFlagBehaviorAppendUnique, ModFlagBehaviorMax, ModFlagBehaviorMin]> { + let cppNamespace = "::mlir::LLVM"; +} + #endif // LLVMIR_ENUMS diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td index b27da58d484a3..90cc851c0a3b2 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td @@ -2183,4 +2183,36 @@ def LLVM_LinkerOptionsOp let hasVerifier = 1; } +//===--------------------------------------------------------------------===// +// ModuleFlagsOp +//===--------------------------------------------------------------------===// + +def LLVM_ModuleFlagsOp + : LLVM_Op<"module_flags"> { + let summary = "Information about module properties"; + let description = [{ + Represents the equivalent in MLIR for LLVM's `llvm.module.flags` metadata, + which requires a list of metadata triplets. Each triplet entry is described + by a `ModuleFlagAttr`. + + Example: + ```mlir + llvm.module.flags [ + #llvm.mlir.module_flag, + #llvm.mlir.module_flag + ] + ``` + }]; + let arguments = (ins ArrayAttr:$flags); + let assemblyFormat = [{ + $flags attr-dict + }]; + + let llvmBuilder = [{ + convertModuleFlagsOp($flags, 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 a4d108e349c00..b766b1710ad80 100644 --- a/mlir/include/mlir/Target/LLVMIR/ModuleImport.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleImport.h @@ -218,6 +218,9 @@ class ModuleImport { /// LLVM dialect operation. LogicalResult convertLinkerOptionsMetadata(); + /// Converts !llvm.module.flags metadata. + LogicalResult convertModuleFlagsMetadata(); + /// Converts !llvm.ident metadata to the llvm.ident LLVM ModuleOp attribute. LogicalResult convertIdentMetadata(); diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp index 239dcbd8c5d19..5370de501a85c 100644 --- a/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp +++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp @@ -3712,6 +3712,20 @@ LogicalResult LinkerOptionsOp::verify() { return success(); } +//===----------------------------------------------------------------------===// +// ModuleFlagsOp +//===----------------------------------------------------------------------===// + +LogicalResult ModuleFlagsOp::verify() { + if (Operation *parentOp = (*this)->getParentOp(); + parentOp && !satisfiesLLVMModule(parentOp)) + return emitOpError("must appear at the module level"); + for (Attribute flag : getFlags()) + if (!isa(flag)) + return emitOpError("expected a module flag attribute"); + return success(); +} + //===----------------------------------------------------------------------===// // InlineAsmOp //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp index 25599efe64322..833b19c1bece2 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp @@ -270,6 +270,15 @@ static void convertLinkerOptionsOp(ArrayAttr options, linkerMDNode->addOperand(listMDNode); } +static void convertModuleFlagsOp(ArrayAttr flags, llvm::IRBuilderBase &builder, + LLVM::ModuleTranslation &moduleTranslation) { + llvm::Module *llvmModule = moduleTranslation.getLLVMModule(); + for (auto flagAttr : flags.getAsRange()) + llvmModule->addModuleFlag( + convertModFlagBehaviorToLLVM(flagAttr.getBehavior()), + flagAttr.getKey().getValue(), flagAttr.getValue()); +} + 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 ab187cd05de75..663cad7d3cd24 100644 --- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp @@ -517,6 +517,33 @@ void ModuleImport::addDebugIntrinsic(llvm::CallInst *intrinsic) { debugIntrinsics.insert(intrinsic); } +LogicalResult ModuleImport::convertModuleFlagsMetadata() { + SmallVector llvmModuleFlags; + llvmModule->getModuleFlagsMetadata(llvmModuleFlags); + + SmallVector moduleFlags; + for (const auto [behavior, key, val] : llvmModuleFlags) { + // Currently only supports most common: int constant values. + auto *constInt = llvm::mdconst::dyn_extract(val); + if (!constInt) { + emitWarning(mlirModule.getLoc()) + << "unsupported module flag value: " << diagMD(val, llvmModule.get()) + << ", only constant integer currently supported"; + continue; + } + + moduleFlags.push_back(builder.getAttr( + convertModFlagBehaviorFromLLVM(behavior), + builder.getStringAttr(key->getString()), constInt->getZExtValue())); + } + + if (!moduleFlags.empty()) + builder.create(mlirModule.getLoc(), + builder.getArrayAttr(moduleFlags)); + + return success(); +} + LogicalResult ModuleImport::convertLinkerOptionsMetadata() { for (const llvm::NamedMDNode &named : llvmModule->named_metadata()) { if (named.getName() != "llvm.linker.options") @@ -596,6 +623,8 @@ LogicalResult ModuleImport::convertMetadata() { } if (failed(convertLinkerOptionsMetadata())) return failure(); + if (failed(convertModuleFlagsMetadata())) + return failure(); if (failed(convertIdentMetadata())) return failure(); if (failed(convertCommandlineMetadata())) diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir index fcb6ae07f4912..6d3d3937b651c 100644 --- a/mlir/test/Dialect/LLVMIR/invalid.mlir +++ b/mlir/test/Dialect/LLVMIR/invalid.mlir @@ -1764,3 +1764,10 @@ llvm.mlir.alias external @y5 : i32 { llvm.return %0 : !llvm.ptr<4> } +// ----- + +module { + // expected-error@+2 {{expected integer value}} + // expected-error@+1 {{failed to parse ModuleFlagAttr parameter 'value' which is to be a `uint32_t`}} + llvm.module_flags [#llvm.mlir.module_flag] +} diff --git a/mlir/test/Dialect/LLVMIR/module-roundtrip.mlir b/mlir/test/Dialect/LLVMIR/module-roundtrip.mlir new file mode 100644 index 0000000000000..d99a93c1e8565 --- /dev/null +++ b/mlir/test/Dialect/LLVMIR/module-roundtrip.mlir @@ -0,0 +1,16 @@ +// RUN: mlir-opt %s | mlir-opt | FileCheck %s + +module { + llvm.module_flags [#llvm.mlir.module_flag, + #llvm.mlir.module_flag, + #llvm.mlir.module_flag, + #llvm.mlir.module_flag, + #llvm.mlir.module_flag] +} + +// CHECK: llvm.module_flags [ +// CHECK-SAME: #llvm.mlir.module_flag, +// CHECK-SAME: #llvm.mlir.module_flag, +// CHECK-SAME: #llvm.mlir.module_flag, +// CHECK-SAME: #llvm.mlir.module_flag, +// CHECK-SAME: #llvm.mlir.module_flag] diff --git a/mlir/test/Target/LLVMIR/Import/module-flags.ll b/mlir/test/Target/LLVMIR/Import/module-flags.ll new file mode 100644 index 0000000000000..b7b686f94c7f4 --- /dev/null +++ b/mlir/test/Target/LLVMIR/Import/module-flags.ll @@ -0,0 +1,25 @@ +; RUN: mlir-translate -import-llvm -split-input-file -verify-diagnostics %s | FileCheck %s + +!llvm.module.flags = !{!0, !1, !2, !3, !4} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 8, !"PIC Level", i32 2} +!2 = !{i32 7, !"PIE Level", i32 2} +!3 = !{i32 7, !"uwtable", i32 2} +!4 = !{i32 7, !"frame-pointer", i32 1} + +; CHECK-LABEL: module attributes {{.*}} { +; CHECK: llvm.module_flags [ +; CHECK-SAME: #llvm.mlir.module_flag, +; CHECK-SAME: #llvm.mlir.module_flag, +; CHECK-SAME: #llvm.mlir.module_flag, +; CHECK-SAME: #llvm.mlir.module_flag, +; CHECK-SAME: #llvm.mlir.module_flag] +; CHECK: } + +; // ----- + +!llvm.module.flags = !{!0} + +; expected-warning@-5{{unsupported module flag value: !"yolo_more", only constant integer currently supported}} +!0 = !{i32 1, !"yolo", !"yolo_more"} diff --git a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir index 15658ea606812..f755c4e508c22 100644 --- a/mlir/test/Target/LLVMIR/llvmir-invalid.mlir +++ b/mlir/test/Target/LLVMIR/llvmir-invalid.mlir @@ -348,6 +348,20 @@ llvm.func @foo() { // ----- +llvm.func @foo() { + // expected-error @below{{must appear at the module level}} + llvm.module_flags [#llvm.mlir.module_flag] +} + +// ----- + +module attributes {} { + // expected-error @below{{expected a module flag attribute}} + llvm.module_flags [4 : i32] +} + +// ----- + module @does_not_exist { // expected-error @below{{resource does not exist}} llvm.mlir.global internal constant @constant(dense_resource : tensor<4xf32>) : !llvm.array<4 x f32> diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir index f9473bf112387..ca06b26b03409 100644 --- a/mlir/test/Target/LLVMIR/llvmir.mlir +++ b/mlir/test/Target/LLVMIR/llvmir.mlir @@ -2763,3 +2763,22 @@ llvm.func @call_intrin_with_opbundle(%arg0 : !llvm.ptr) { // CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr %0, i32 16) ] // CHECK-NEXT: ret void // CHECK-NEXT: } + +// ----- + +module { + llvm.module_flags [#llvm.mlir.module_flag, + #llvm.mlir.module_flag, + #llvm.mlir.module_flag, + #llvm.mlir.module_flag, + #llvm.mlir.module_flag] +} + +// CHECK: !llvm.module.flags = !{![[#DBG:]], ![[#WCHAR:]], ![[#PIC:]], ![[#PIE:]], ![[#UWTABLE:]], ![[#FrameP:]]} + +// CHECK: ![[#DBG]] = !{i32 2, !"Debug Info Version", i32 3} +// CHECK: ![[#WCHAR]] = !{i32 1, !"wchar_size", i32 4} +// CHECK: ![[#PIC]] = !{i32 8, !"PIC Level", i32 2} +// CHECK: ![[#PIE]] = !{i32 7, !"PIE Level", i32 2} +// CHECK: ![[#UWTABLE]] = !{i32 7, !"uwtable", i32 2} +// CHECK: ![[#FrameP]] = !{i32 7, !"frame-pointer", i32 1} diff --git a/mlir/test/mlir-translate/split-markers.mlir b/mlir/test/mlir-translate/split-markers.mlir index ed576bcd85236..cf950eb12ec5a 100644 --- a/mlir/test/mlir-translate/split-markers.mlir +++ b/mlir/test/mlir-translate/split-markers.mlir @@ -21,6 +21,8 @@ // CHECK-OUTPUT-NEXT: ModuleID // CHECK-ROUNDTRIP: module {{.*}} { +// FIXME: importer forces debug info version even without importing one. +// CHECK-ROUNDTRIP-NEXT: llvm.module_flag // CHECK-ROUNDTRIP-NEXT: } // CHECK-ROUNDTRIP-EMPTY: // CHECK-ROUNDTRIP: module