[Mlir-commits] [mlir] [MLIR][NVVM] Add support for pzo in f32x2 to fp16x2 conversions (PR #237857)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Sun Nov 1 23:46:39 PST 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Srinivasa Ravi (Wolfram70)

<details>
<summary>Changes</summary>

This change adds the `pzo` attribute to the MLIR
`nvvm.convert.f32x2.to.(b)f16x2` Ops, lowering to the `.pzo` variant of the `cvt` instructions.

PTX Spec Reference: https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cvt

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


4 Files Affected:

- (modified) mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td (+7-3) 
- (modified) mlir/lib/Dialect/LLVMIR/IR/NVVMDialect.cpp (+10-10) 
- (modified) mlir/test/Target/LLVMIR/nvvm/convert_fp16x2.mlir (+28) 
- (modified) mlir/test/Target/LLVMIR/nvvm/invalid_convert_fp16x2.mlir (+16) 


``````````diff
diff --git a/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td
index ccc7805dd6ae1..e53aee9a38800 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td
@@ -2662,7 +2662,8 @@ class NVVM_ConvertF32x2ToFPx2OpBase<string dstFormat, string mnemonic, Type dstT
                  Optional<I32>:$random_bits,
                  DefaultValuedAttr<FPRoundingModeAttr, "FPRoundingMode::NONE">:$rnd,
                  DefaultValuedAttr<SaturationModeAttr, "SaturationMode::NONE">:$sat,
-                 DefaultValuedAttr<BoolAttr, "false">:$relu)> {
+                 DefaultValuedAttr<BoolAttr, "false">:$relu,
+                 DefaultValuedAttr<BoolAttr, "false">:$pzo)> {
   let summary = "Convert two F32 values to packed " # !tolower(dstFormat) # ".";
   let description = [{
     Converts two F32 values to packed }] # !tolower(dstFormat) # [{ format with 
@@ -2679,14 +2680,17 @@ class NVVM_ConvertF32x2ToFPx2OpBase<string dstFormat, string mnemonic, Type dstT
     The `relu` attribute clamps negative results to 0.
 
     The `sat` attribute determines saturation behavior.
-    
+
+    The `pzo` attribute, when set, lowers to the '.pzo' variant of the cvt
+    instruction. `pzo` is not supported with the RS rounding mode.
+
     [For more information, see PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cvt)
   }];
   
   let assemblyFormat = [{
     $src_hi `,` $src_lo oilist(
       `random_bits` `=` $random_bits | `rnd` `=` $rnd
-      | `sat` `=` $sat | `relu` `=` $relu
+      | `sat` `=` $sat | `relu` `=` $relu | `pzo` `=` $pzo
     )
     attr-dict `:` type($dst)
   }];
diff --git a/mlir/lib/Dialect/LLVMIR/IR/NVVMDialect.cpp b/mlir/lib/Dialect/LLVMIR/IR/NVVMDialect.cpp
index bd3e18946393b..d214309fafb11 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/NVVMDialect.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/NVVMDialect.cpp
@@ -710,7 +710,7 @@ LogicalResult PermuteOp::verify() {
 static LogicalResult verifyConvertF32x2ToFP16x2Op(Twine dstType,
                                                   FPRoundingMode rnd,
                                                   bool hasRandomBits,
-                                                  Operation *op) {
+                                                  bool hasPzo, Operation *op) {
   static constexpr FPRoundingMode validRndModes[] = {
       FPRoundingMode::RN, FPRoundingMode::RZ, FPRoundingMode::RS};
 
@@ -725,6 +725,9 @@ static LogicalResult verifyConvertF32x2ToFP16x2Op(Twine dstType,
     if (!hasRandomBits) {
       return op->emitOpError("random_bits is required for RS rounding mode.");
     }
+    if (hasPzo) {
+      return op->emitOpError("pzo not supported for RS rounding mode.");
+    }
   } else {
     if (hasRandomBits) {
       return op->emitOpError(
@@ -736,13 +739,13 @@ static LogicalResult verifyConvertF32x2ToFP16x2Op(Twine dstType,
 }
 
 LogicalResult ConvertF32x2ToF16x2Op::verify() {
-  return verifyConvertF32x2ToFP16x2Op("f16x2", getRnd(),
-                                      getRandomBits() ? true : false, *this);
+  return verifyConvertF32x2ToFP16x2Op(
+      "f16x2", getRnd(), getRandomBits() ? true : false, getPzo(), *this);
 }
 
 LogicalResult ConvertF32x2ToBF16x2Op::verify() {
-  return verifyConvertF32x2ToFP16x2Op("bf16x2", getRnd(),
-                                      getRandomBits() ? true : false, *this);
+  return verifyConvertF32x2ToFP16x2Op(
+      "bf16x2", getRnd(), getRandomBits() ? true : false, getPzo(), *this);
 }
 
 LogicalResult ConvertF32x4ToF8x4Op::verify() {
@@ -5750,10 +5753,8 @@ ConvertF32x2ToF16x2Op::getIntrinsicIDAndArgs(NVVM::ConvertF32x2ToF16x2Op &op,
   if (op.getRandomBits())
     args.push_back(mt.lookupValue(op.getRandomBits()));
 
-  // The PZO modifier is not supported with the RS rounding mode.
-  // TODO: Add support for PZO modifier
   if (op.getRnd() != FPRoundingMode::RS)
-    args.push_back(builder.getInt1(false));
+    args.push_back(builder.getInt1(op.getPzo()));
 
   switch (op.getRnd()) {
   case FPRoundingMode::RN:
@@ -5803,9 +5804,8 @@ ConvertF32x2ToBF16x2Op::getIntrinsicIDAndArgs(NVVM::ConvertF32x2ToBF16x2Op &op,
   if (op.getRandomBits())
     args.push_back(mt.lookupValue(op.getRandomBits()));
 
-  // TODO: Add support for PZO modifier
   if (op.getRnd() != FPRoundingMode::RS)
-    args.push_back(builder.getInt1(false));
+    args.push_back(builder.getInt1(op.getPzo()));
 
   switch (op.getRnd()) {
   case FPRoundingMode::RN:
diff --git a/mlir/test/Target/LLVMIR/nvvm/convert_fp16x2.mlir b/mlir/test/Target/LLVMIR/nvvm/convert_fp16x2.mlir
index 6338f6a15030e..f01189af06aaf 100644
--- a/mlir/test/Target/LLVMIR/nvvm/convert_fp16x2.mlir
+++ b/mlir/test/Target/LLVMIR/nvvm/convert_fp16x2.mlir
@@ -42,6 +42,20 @@ llvm.func @convert_f32x2_to_f16x2_rs_stochastic(%srcA : f32, %srcB : f32, %rbits
   llvm.return
 }
 
+// CHECK-LABEL: @convert_f32x2_to_f16x2_pzo
+llvm.func @convert_f32x2_to_f16x2_pzo(%srcA : f32, %srcB : f32) {
+  // CHECK: %{{.*}} = call <2 x half> @llvm.nvvm.ff2f16x2.rn(float %{{.*}}, float %{{.*}}, i1 true)
+  %res1 = nvvm.convert.f32x2.to.f16x2 %srcA, %srcB rnd = <rn> pzo = true : vector<2xf16>
+  // CHECK: %{{.*}} = call <2 x half> @llvm.nvvm.ff2f16x2.rn.relu.satfinite(float %{{.*}}, float %{{.*}}, i1 true)
+  %res2 = nvvm.convert.f32x2.to.f16x2 %srcA, %srcB rnd = <rn> sat = <satfinite> relu = true pzo = true : vector<2xf16>
+  // CHECK: %{{.*}} = call <2 x half> @llvm.nvvm.ff2f16x2.rz(float %{{.*}}, float %{{.*}}, i1 true)
+  %res3 = nvvm.convert.f32x2.to.f16x2 %srcA, %srcB rnd = <rz> pzo = true : vector<2xf16>
+  // CHECK: %{{.*}} = call <2 x half> @llvm.nvvm.ff2f16x2.rz.relu.satfinite(float %{{.*}}, float %{{.*}}, i1 true)
+  %res4 = nvvm.convert.f32x2.to.f16x2 %srcA, %srcB rnd = <rz> sat = <satfinite> relu = true pzo = true : vector<2xf16>
+
+  llvm.return
+}
+
 // -----
 
 // CHECK-LABEL: @convert_f32x2_to_bf16x2_rn
@@ -85,3 +99,17 @@ llvm.func @convert_f32x2_to_bf16x2_rs_stochastic(%srcA : f32, %srcB : f32, %rbit
 
   llvm.return
 }
+
+// CHECK-LABEL: @convert_f32x2_to_bf16x2_pzo
+llvm.func @convert_f32x2_to_bf16x2_pzo(%srcA : f32, %srcB : f32) {
+  // CHECK: %{{.*}} = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rn(float %{{.*}}, float %{{.*}}, i1 true)
+  %res1 = nvvm.convert.f32x2.to.bf16x2 %srcA, %srcB rnd = <rn> pzo = true : vector<2xbf16>
+  // CHECK: %{{.*}} = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rn.relu.satfinite(float %{{.*}}, float %{{.*}}, i1 true)
+  %res2 = nvvm.convert.f32x2.to.bf16x2 %srcA, %srcB rnd = <rn> sat = <satfinite> relu = true pzo = true : vector<2xbf16>
+  // CHECK: %{{.*}} = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz(float %{{.*}}, float %{{.*}}, i1 true)
+  %res3 = nvvm.convert.f32x2.to.bf16x2 %srcA, %srcB rnd = <rz> pzo = true : vector<2xbf16>
+  // CHECK: %{{.*}} = call <2 x bfloat> @llvm.nvvm.ff2bf16x2.rz.relu.satfinite(float %{{.*}}, float %{{.*}}, i1 true)
+  %res4 = nvvm.convert.f32x2.to.bf16x2 %srcA, %srcB rnd = <rz> sat = <satfinite> relu = true pzo = true : vector<2xbf16>
+
+  llvm.return
+}
diff --git a/mlir/test/Target/LLVMIR/nvvm/invalid_convert_fp16x2.mlir b/mlir/test/Target/LLVMIR/nvvm/invalid_convert_fp16x2.mlir
index 4ec257f550f7f..f669d0d3e39cf 100644
--- a/mlir/test/Target/LLVMIR/nvvm/invalid_convert_fp16x2.mlir
+++ b/mlir/test/Target/LLVMIR/nvvm/invalid_convert_fp16x2.mlir
@@ -26,6 +26,14 @@ llvm.func @nvvm_cvt_f32x2_to_f16x2_invalid_rbits_2(%a : f32, %b : f32, %rbits :
 
 // -----
 
+llvm.func @nvvm_cvt_f32x2_to_f16x2_invalid_pzo(%a : f32, %b : f32, %rbits : i32) {
+  // expected-error @below {{pzo not supported for RS rounding mode.}}
+  %res = nvvm.convert.f32x2.to.f16x2 %a, %b random_bits = %rbits rnd = <rs> pzo = true : vector<2xf16>
+  llvm.return
+}
+
+// -----
+
 llvm.func @nvvm_cvt_f32x2_to_bf16x2_invalid_rounding(%a : f32, %b : f32) {
   // expected-error @below {{Only RN, RZ, and RS rounding modes are supported for conversions from f32x2 to bf16x2.}}
   %res = nvvm.convert.f32x2.to.bf16x2 %a, %b rnd = <rm> : vector<2xbf16>
@@ -45,3 +53,11 @@ llvm.func @nvvm_cvt_f32x2_to_bf16x2_invalid_rbits_2(%a : f32, %b : f32, %rbits :
   %res = nvvm.convert.f32x2.to.bf16x2 %a, %b random_bits = %rbits rnd = <rn> : vector<2xbf16>
   llvm.return
 }
+
+// -----
+
+llvm.func @nvvm_cvt_f32x2_to_bf16x2_invalid_pzo(%a : f32, %b : f32, %rbits : i32) {
+  // expected-error @below {{pzo not supported for RS rounding mode.}}
+  %res = nvvm.convert.f32x2.to.bf16x2 %a, %b random_bits = %rbits rnd = <rs> pzo = true : vector<2xbf16>
+  llvm.return
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/237857


More information about the Mlir-commits mailing list