[Mlir-commits] [mlir] [NVVM][MLIR] Fixed valgrind leak in MMAOp (PR #208063)

Stefan Mada llvmlistbot at llvm.org
Wed Jul 8 08:31:40 PDT 2026


https://github.com/smada3 updated https://github.com/llvm/llvm-project/pull/208063

>From 5458e4a6a9d12bab08793042addb0b87cf91117e Mon Sep 17 00:00:00 2001
From: Stefan Mada <smada at nvidia.com>
Date: Tue, 7 Jul 2026 18:20:31 +0000
Subject: [PATCH 1/2] [NVVM][MLIR] Fixed valgrind leak in MMAOp

---
 mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td | 22 +++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td
index 40f7f15b694cb..1f5738589a6a0 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td
@@ -3384,9 +3384,9 @@ def NVVM_MmaOp : NVVM_Op<"mma.sync", [AttrSizedOperandSegments]> {
     `intOverflowBehavior` is only relevant when the `multiplicandType` attribute
     is one of `u8, s8, u4, s4`, this attribute describes how overflow is handled
     in the accumulator. When the attribute is `satfinite`, the accumulator values
-    are clamped in the int32 range on overflow. This is the default behavior.
-    Alternatively, accumulator behavior `wrapped` can also be specified, in
-    which case overflow wraps from one end of the range to the other.
+    are clamped in the int32 range on overflow. Alternatively, accumulator
+    behavior `wrapped` can be specified (this is the default), in which case
+    overflow wraps from one end of the range to the other.
 
     `layoutA` and `layoutB` are required and should generally be set to
     `#nvvm.mma_layout<row>` and `#nvvm.mma_layout<col>` respectively, but other
@@ -3477,9 +3477,23 @@ def NVVM_MmaOp : NVVM_Op<"mma.sync", [AttrSizedOperandSegments]> {
 
   string llvmBuilder = [{
     auto operands = moduleTranslation.lookupValues(opInst.getOperands());
+    // `intOverflowBehavior` is optional; an omitted attribute selects the
+    // default `wrapped` behavior. Resolve it to a fully-initialized value here
+    // so the intrinsic selector always inspects an engaged optional and never
+    // reads an empty optional's uninitialized payload (a benign read the
+    // optimizer can hoist out of the guarding `has_value()` check under -O2).
+    // An empty std::optional still reserves storage for its payload but leaves
+    // it uninitialized, and at -O2 the optimizer may if-convert the selector's
+    // `has_value() ? ... *sat ... : true` guard and speculatively load `*sat`
+    // before the guard discards it. This is not UB -- the read touches validly
+    // allocated storage and its result is never used -- but Valgrind still
+    // reports the load of uninitialized bytes.
+    std::optional<mlir::NVVM::MMAIntOverflow> intOverflow = mlir::NVVM::MMAIntOverflow::wrapped;
+    if (mlir::NVVM::MMAIntOverflowAttr satAttr = op.getIntOverflowBehaviorAttr())
+      intOverflow = satAttr.getValue();
     auto intId = mlir::NVVM::MmaOp::getIntrinsicID(
         $shape.getM(), $shape.getN(), $shape.getK(),
-        $b1Op, $intOverflowBehavior,
+        $b1Op, intOverflow,
         $layoutA, $layoutB,
         *$multiplicandAPtxType,
         *$multiplicandBPtxType,

>From 964544ea817f23d0a055d14fcfc72c5493755475 Mon Sep 17 00:00:00 2001
From: Stefan Mada <smada at nvidia.com>
Date: Wed, 8 Jul 2026 15:31:25 +0000
Subject: [PATCH 2/2] Shortened comment on mmaop valgrind fix

---
 mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td | 15 ++++-----------
 1 file changed, 4 insertions(+), 11 deletions(-)

diff --git a/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td
index 1f5738589a6a0..627d9a861d780 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td
@@ -3477,17 +3477,10 @@ def NVVM_MmaOp : NVVM_Op<"mma.sync", [AttrSizedOperandSegments]> {
 
   string llvmBuilder = [{
     auto operands = moduleTranslation.lookupValues(opInst.getOperands());
-    // `intOverflowBehavior` is optional; an omitted attribute selects the
-    // default `wrapped` behavior. Resolve it to a fully-initialized value here
-    // so the intrinsic selector always inspects an engaged optional and never
-    // reads an empty optional's uninitialized payload (a benign read the
-    // optimizer can hoist out of the guarding `has_value()` check under -O2).
-    // An empty std::optional still reserves storage for its payload but leaves
-    // it uninitialized, and at -O2 the optimizer may if-convert the selector's
-    // `has_value() ? ... *sat ... : true` guard and speculatively load `*sat`
-    // before the guard discards it. This is not UB -- the read touches validly
-    // allocated storage and its result is never used -- but Valgrind still
-    // reports the load of uninitialized bytes.
+    // `intOverflowBehavior` is optional and defaults to `wrapped` when omitted.
+    // We initialize it to a concrete value so that the intrinsic selector never
+    // speculatively reads an empty optional's uninitialized payload (benign,
+    // but flagged by Valgrind).
     std::optional<mlir::NVVM::MMAIntOverflow> intOverflow = mlir::NVVM::MMAIntOverflow::wrapped;
     if (mlir::NVVM::MMAIntOverflowAttr satAttr = op.getIntOverflowBehaviorAttr())
       intOverflow = satAttr.getValue();



More information about the Mlir-commits mailing list