Skip to content

[MLIR][LLVM] Add CG Profile module flags support #137115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

bcardosolopes
Copy link
Member

Dialect only accept arbitrary module flag values in face of simple types like int and string. Whenever metadata is a bit more complex use specific attributes to map functionality. This PR adds an attribute to represent "CG Profile" entries, verifiers, import / translate support.

@llvmbot
Copy link
Member

llvmbot commented Apr 24, 2025

@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-llvm

Author: Bruno Cardoso Lopes (bcardosolopes)

Changes

Dialect only accept arbitrary module flag values in face of simple types like int and string. Whenever metadata is a bit more complex use specific attributes to map functionality. This PR adds an attribute to represent "CG Profile" entries, verifiers, import / translate support.


Full diff: https://github.com/llvm/llvm-project/pull/137115.diff

8 Files Affected:

  • (modified) mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td (+33-6)
  • (modified) mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp (+16-4)
  • (modified) mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp (+30)
  • (modified) mlir/lib/Target/LLVMIR/ModuleImport.cpp (+39-1)
  • (modified) mlir/test/Dialect/LLVMIR/invalid.mlir (+16)
  • (modified) mlir/test/Dialect/LLVMIR/module-roundtrip.mlir (+12-2)
  • (modified) mlir/test/Target/LLVMIR/Import/module-flags.ll (+19)
  • (modified) mlir/test/Target/LLVMIR/llvmir.mlir (+21)
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
index f53f95ee9ba49..cfa6ebf3e6775 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
@@ -1322,7 +1322,7 @@ def LLVM_DereferenceableAttr : LLVM_Attr<"Dereferenceable", "dereferenceable"> {
 }
 
 //===----------------------------------------------------------------------===//
-// ModuleFlagAttr
+// ModuleFlagAttr & related
 //===----------------------------------------------------------------------===//
 
 def ModuleFlagAttr
@@ -1332,14 +1332,22 @@ def ModuleFlagAttr
     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. Current supported types of values:
-      - Integer constants
-      - Strings
+    and third is the value of the flag. Supported keys and values include:
+      - Arbitrary `key`s holding integer constants or strings.
+      - Domain specific keys (e.g "CG Profile"), holding lists of supported
+        module flag values (e.g. `llvm.cgprofile_entry`).
 
     Example:
     ```mlir
-      #llvm.mlir.module_flag<error, "wchar_size", 4>
-      #llvm.mlir.module_flag<error, "probe-stack", "inline-asm">
+      llvm.module_flags [
+          #llvm.mlir.module_flag<error, "wchar_size", 4>,
+          #llvm.mlir.module_flag<error, "probe-stack", "inline-asm">,
+          #llvm.mlir.module_flag<append, "CG Profile", [
+            #llvm.cgprofile_entry<from = @from, to = @to, count = 222>,
+            #llvm.cgprofile_entry<from = @from, to = @from, count = 222>,
+            #llvm.cgprofile_entry<from = @to, to = @from, count = 222>
+          ]
+      >]
     ```
   }];
   let parameters = (ins "ModFlagBehavior":$behavior,
@@ -1349,6 +1357,25 @@ def ModuleFlagAttr
   let genVerifyDecl = 1;
 }
 
+def ModuleFlagCGProfileEntryAttr
+    : LLVM_Attr<"ModuleFlagCGProfileEntry", "cgprofile_entry"> {
+  let summary = "CG profile module flag entry";
+  let description = [{
+    Describes a single entry for a CG profile module flag. Example:
+    ```mlir
+      llvm.module_flags [
+        #llvm.mlir.module_flag<append, "CG Profile",
+          [#llvm.cgprofile_entry<from = @from, to = @to, count = 222>,
+           ...
+          ]>]
+    ```
+  }];
+  let parameters = (ins "FlatSymbolRefAttr":$from,
+                        "FlatSymbolRefAttr":$to,
+                        "uint64_t":$count);
+  let assemblyFormat = "`<` struct(params) `>`";
+}
+
 //===----------------------------------------------------------------------===//
 // LLVM_DependentLibrariesAttr
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
index f3ebb8a565ea4..353108e95c8e2 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMAttrs.cpp
@@ -380,8 +380,20 @@ LogicalResult
 ModuleFlagAttr::verify(function_ref<InFlightDiagnostic()> emitError,
                        LLVM::ModFlagBehavior flagBehavior, StringAttr key,
                        Attribute value) {
-  if (!isa<IntegerAttr, StringAttr>(value))
-    return emitError()
-           << "only integer and string values are currently supported";
-  return success();
+  if (key == "CG Profile") {
+    auto arrayAttr = dyn_cast<ArrayAttr>(value);
+    if ((!arrayAttr) || (!llvm::all_of(arrayAttr, [](Attribute v) {
+          return isa<ModuleFlagCGProfileEntryAttr>(v);
+        })))
+      return emitError()
+             << "'CG Profile' key expects an array of '#llvm.cgprofile_entry'";
+    return success();
+  }
+
+  if (isa<IntegerAttr, StringAttr>(value))
+    return success();
+
+  return emitError() << "only integer and string values are currently "
+                        "supported for unknown key '"
+                     << key << "'";
 }
diff --git a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
index e816a3e218452..634d052e0d221 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp
@@ -271,6 +271,31 @@ static void convertLinkerOptionsOp(ArrayAttr options,
   linkerMDNode->addOperand(listMDNode);
 }
 
+static llvm::Metadata *
+convertModuleFlagValue(StringRef key, ArrayAttr arrayAttr,
+                       llvm::IRBuilderBase &builder,
+                       LLVM::ModuleTranslation &moduleTranslation) {
+  llvm::LLVMContext &context = builder.getContext();
+  llvm::MDBuilder mdb(context);
+  SmallVector<llvm::Metadata *> nodes;
+
+  if (key == "CG Profile") {
+    for (auto entry : arrayAttr.getAsRange<ModuleFlagCGProfileEntryAttr>()) {
+      llvm::Function *fromFn =
+          moduleTranslation.lookupFunction(entry.getFrom().getValue());
+      llvm::Function *toFn =
+          moduleTranslation.lookupFunction(entry.getTo().getValue());
+      llvm::Metadata *vals[] = {
+          llvm::ValueAsMetadata::get(fromFn), llvm::ValueAsMetadata::get(toFn),
+          mdb.createConstant(llvm::ConstantInt::get(
+              llvm::Type::getInt64Ty(context), entry.getCount()))};
+      nodes.push_back(llvm::MDNode::get(context, vals));
+    }
+    return llvm::MDTuple::getDistinct(context, nodes);
+  }
+  return nullptr;
+}
+
 static void convertModuleFlagsOp(ArrayAttr flags, llvm::IRBuilderBase &builder,
                                  LLVM::ModuleTranslation &moduleTranslation) {
   llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
@@ -286,6 +311,11 @@ static void convertModuleFlagsOp(ArrayAttr flags, llvm::IRBuilderBase &builder,
                   llvm::Type::getInt32Ty(builder.getContext()),
                   intAttr.getInt()));
             })
+            .Case<ArrayAttr>([&](auto arrayAttr) {
+              return convertModuleFlagValue(flagAttr.getKey().getValue(),
+                                            arrayAttr, builder,
+                                            moduleTranslation);
+            })
             .Default([](auto) { return nullptr; });
 
     assert(valueMetadata && "expected valid metadata");
diff --git a/mlir/lib/Target/LLVMIR/ModuleImport.cpp b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
index 3f80002c15ebb..5171e19d80d9b 100644
--- a/mlir/lib/Target/LLVMIR/ModuleImport.cpp
+++ b/mlir/lib/Target/LLVMIR/ModuleImport.cpp
@@ -519,6 +519,39 @@ void ModuleImport::addDebugIntrinsic(llvm::CallInst *intrinsic) {
   debugIntrinsics.insert(intrinsic);
 }
 
+static Attribute convertCGProfileModuleFlagValue(ModuleOp mlirModule,
+                                                 llvm::MDTuple *mdTuple) {
+  auto getFunctionSymbol = [&](const llvm::MDOperand &funcMDO) {
+    auto *f = cast<llvm::ValueAsMetadata>(funcMDO);
+    auto *llvmFn = cast<llvm::Function>(f->getValue()->stripPointerCasts());
+    return FlatSymbolRefAttr::get(mlirModule->getContext(), llvmFn->getName());
+  };
+
+  // Each tuple element becomes one ModuleFlagCGProfileEntryAttr.
+  SmallVector<Attribute> cgProfile;
+  for (unsigned i = 0; i < mdTuple->getNumOperands(); i++) {
+    const llvm::MDOperand &mdo = mdTuple->getOperand(i);
+    auto *cgEntry = dyn_cast_or_null<llvm::MDNode>(mdo);
+    llvm::Constant *llvmConstant =
+        cast<llvm::ConstantAsMetadata>(cgEntry->getOperand(2))->getValue();
+    uint64_t count = cast<llvm::ConstantInt>(llvmConstant)->getZExtValue();
+    cgProfile.push_back(ModuleFlagCGProfileEntryAttr::get(
+        mlirModule->getContext(), getFunctionSymbol(cgEntry->getOperand(0)),
+        getFunctionSymbol(cgEntry->getOperand(1)), count));
+  }
+  return ArrayAttr::get(mlirModule->getContext(), cgProfile);
+}
+
+// Invoke specific handlers for each known module flag value, returns nullptr if
+// the key is unknown or unimplemented.
+static Attribute convertModuleFlagValueFromMDTuple(ModuleOp mlirModule,
+                                                   StringRef key,
+                                                   llvm::MDTuple *mdTuple) {
+  if (key == "CG Profile")
+    return convertCGProfileModuleFlagValue(mlirModule, mdTuple);
+  return nullptr;
+}
+
 LogicalResult ModuleImport::convertModuleFlagsMetadata() {
   SmallVector<llvm::Module::ModuleFlagEntry> llvmModuleFlags;
   llvmModule->getModuleFlagsMetadata(llvmModuleFlags);
@@ -530,7 +563,12 @@ LogicalResult ModuleImport::convertModuleFlagsMetadata() {
       valAttr = builder.getI32IntegerAttr(constInt->getZExtValue());
     } else if (auto *mdString = dyn_cast<llvm::MDString>(val)) {
       valAttr = builder.getStringAttr(mdString->getString());
-    } else {
+    } else if (auto *mdTuple = dyn_cast<llvm::MDTuple>(val)) {
+      valAttr = convertModuleFlagValueFromMDTuple(mlirModule, key->getString(),
+                                                  mdTuple);
+    }
+
+    if (!valAttr) {
       emitWarning(mlirModule.getLoc())
           << "unsupported module flag value: " << diagMD(val, llvmModule.get());
       continue;
diff --git a/mlir/test/Dialect/LLVMIR/invalid.mlir b/mlir/test/Dialect/LLVMIR/invalid.mlir
index a3cd9572933ae..e8902c4ccd5af 100644
--- a/mlir/test/Dialect/LLVMIR/invalid.mlir
+++ b/mlir/test/Dialect/LLVMIR/invalid.mlir
@@ -1784,6 +1784,22 @@ module {
 
 // -----
 
+module {
+  // expected-error@below {{'CG Profile' key expects an array of '#llvm.cgprofile_entry'}}
+  llvm.module_flags [#llvm.mlir.module_flag<append, "CG Profile", [
+    "yo"
+  ]>]
+}
+
+// -----
+
+module {
+  // expected-error@below {{'CG Profile' key expects an array of '#llvm.cgprofile_entry'}}
+  llvm.module_flags [#llvm.mlir.module_flag<append, "CG Profile", 3 : i64>]
+}
+
+// -----
+
 llvm.func @t0() -> !llvm.ptr {
   %0 = llvm.blockaddress <function = @t0, tag = <id = 1>> : !llvm.ptr
   llvm.blocktag <id = 1>
diff --git a/mlir/test/Dialect/LLVMIR/module-roundtrip.mlir b/mlir/test/Dialect/LLVMIR/module-roundtrip.mlir
index a94514da9818f..508f560ff69f0 100644
--- a/mlir/test/Dialect/LLVMIR/module-roundtrip.mlir
+++ b/mlir/test/Dialect/LLVMIR/module-roundtrip.mlir
@@ -6,7 +6,12 @@ module {
                      #llvm.mlir.module_flag<max, "PIE Level", 2 : i32>,
                      #llvm.mlir.module_flag<max, "uwtable", 2 : i32>,
                      #llvm.mlir.module_flag<max, "frame-pointer", 1 : i32>,
-                     #llvm.mlir.module_flag<override, "probe-stack", "inline-asm">]
+                     #llvm.mlir.module_flag<override, "probe-stack", "inline-asm">,
+                     #llvm.mlir.module_flag<append, "CG Profile", [
+                      #llvm.cgprofile_entry<from = @from, to = @to, count = 222>,
+                      #llvm.cgprofile_entry<from = @from, to = @from, count = 222>,
+                      #llvm.cgprofile_entry<from = @to, to = @from, count = 222>
+                    ]>]
 }
 
 // CHECK: llvm.module_flags [
@@ -15,4 +20,9 @@ module {
 // CHECK-SAME: #llvm.mlir.module_flag<max, "PIE Level", 2 : i32>,
 // CHECK-SAME: #llvm.mlir.module_flag<max, "uwtable", 2 : i32>,
 // CHECK-SAME: #llvm.mlir.module_flag<max, "frame-pointer", 1 : i32>,
-// CHECK-SAME: #llvm.mlir.module_flag<override, "probe-stack", "inline-asm">]
+// CHECK-SAME: #llvm.mlir.module_flag<override, "probe-stack", "inline-asm">,
+// CHECK-SAME: #llvm.mlir.module_flag<append, "CG Profile", [
+// CHECK-SAME: #llvm.cgprofile_entry<from = @from, to = @to, count = 222>,
+// CHECK-SAME: #llvm.cgprofile_entry<from = @from, to = @from, count = 222>,
+// CHECK-SAME: #llvm.cgprofile_entry<from = @to, to = @from, count = 222>
+// CHECK-SAME: ]>]
diff --git a/mlir/test/Target/LLVMIR/Import/module-flags.ll b/mlir/test/Target/LLVMIR/Import/module-flags.ll
index e6bb2c0ffb32d..31ab8afb7ed83 100644
--- a/mlir/test/Target/LLVMIR/Import/module-flags.ll
+++ b/mlir/test/Target/LLVMIR/Import/module-flags.ll
@@ -25,3 +25,22 @@
 !12 = !{ i32 2, !"qux", i32 42 }
 !13 = !{ i32 3, !"qux", !{ !"foo", i32 1 }}
 !llvm.module.flags = !{ !10, !11, !12, !13 }
+
+; // -----
+
+declare void @from(i32)
+declare void @to()
+
+!llvm.module.flags = !{!20}
+
+!20 = !{i32 5, !"CG Profile", !21}
+!21 = distinct !{!22, !23, !24}
+!22 = !{ptr @from, ptr @to, i64 222}
+!23 = !{ptr @from, ptr @from, i64 222}
+!24 = !{ptr @to, ptr @from, i64 222}
+
+; CHECK: llvm.module_flags [#llvm.mlir.module_flag<append, "CG Profile", [
+; CHECK-SAME: #llvm.cgprofile_entry<from = @from, to = @to, count = 222>,
+; CHECK-SAME: #llvm.cgprofile_entry<from = @from, to = @from, count = 222>,
+; CHECK-SAME: #llvm.cgprofile_entry<from = @to, to = @from, count = 222>
+; CHECK-SAME: ]>]
diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir
index 74fa327809864..0675531eadbb7 100644
--- a/mlir/test/Target/LLVMIR/llvmir.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir.mlir
@@ -2838,6 +2838,27 @@ module {
 
 // -----
 
+module {
+  llvm.module_flags [#llvm.mlir.module_flag<append, "CG Profile", [
+    #llvm.cgprofile_entry<from = @from, to = @to, count = 222>,
+    #llvm.cgprofile_entry<from = @from, to = @from, count = 222>,
+    #llvm.cgprofile_entry<from = @to, to = @from, count = 222>
+  ]>]
+  llvm.func @from(i32)
+  llvm.func @to()
+}
+
+// CHECK: !llvm.module.flags = !{![[#CGPROF:]], ![[#DBG:]]}
+
+// CHECK: ![[#CGPROF]] = !{i32 5, !"CG Profile", ![[#LIST:]]}
+// CHECK: ![[#LIST]] = distinct !{![[#ENTRY_A:]], ![[#ENTRY_B:]], ![[#ENTRY_C:]]}
+// CHECK: ![[#ENTRY_A]] = !{ptr @from, ptr @to, i64 222}
+// CHECK: ![[#ENTRY_B]] = !{ptr @from, ptr @from, i64 222}
+// CHECK: ![[#ENTRY_C]] = !{ptr @to, ptr @from, i64 222}
+// CHECK: ![[#DBG]] = !{i32 2, !"Debug Info Version", i32 3}
+
+// -----
+
 module attributes {llvm.dependent_libraries = ["foo", "bar"]} {}
 
 // CHECK: !llvm.dependent-libraries =  !{![[#LIBFOO:]], ![[#LIBBAR:]]}

Copy link
Contributor

@gysit gysit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, LGTM modulo some nits.

Copy link
Contributor

@Dinistro Dinistro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM % some additional nits.

@bcardosolopes
Copy link
Member Author

All comments addressed, thanks folks!

@bcardosolopes bcardosolopes merged commit c0bc775 into llvm:main Apr 24, 2025
11 checks passed
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
Dialect only accept arbitrary module flag values in face of simple types
like int and string. Whenever metadata is a bit more complex use
specific attributes to map functionality. This PR adds an attribute to
represent "CG Profile" entries, verifiers, import / translate support.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
Dialect only accept arbitrary module flag values in face of simple types
like int and string. Whenever metadata is a bit more complex use
specific attributes to map functionality. This PR adds an attribute to
represent "CG Profile" entries, verifiers, import / translate support.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
Dialect only accept arbitrary module flag values in face of simple types
like int and string. Whenever metadata is a bit more complex use
specific attributes to map functionality. This PR adds an attribute to
represent "CG Profile" entries, verifiers, import / translate support.
Ankur-0429 pushed a commit to Ankur-0429/llvm-project that referenced this pull request May 9, 2025
Dialect only accept arbitrary module flag values in face of simple types
like int and string. Whenever metadata is a bit more complex use
specific attributes to map functionality. This PR adds an attribute to
represent "CG Profile" entries, verifiers, import / translate support.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants