[Mlir-commits] [mlir] [MLIR][NVVM] Add support for pzo in f32x2 to fp16x2 conversions (PR #237857)
Srinivasa Ravi
llvmlistbot at llvm.org
Sun Nov 1 23:46:01 PST 2026
https://github.com/Wolfram70 created https://github.com/llvm/llvm-project/pull/237857
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
>From 2588aa08fe1e36f8170f11b949ecf283b22b7a7c Mon Sep 17 00:00:00 2001
From: Srinivasa Ravi <srinivasar at nvidia.com>
Date: Thu, 29 Oct 2026 11:13:51 +0000
Subject: [PATCH] [MLIR][NVVM] Add support for pzo in f32x2 to fp16x2
conversions
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
---
mlir/include/mlir/Dialect/LLVMIR/NVVMOps.td | 10 +++++--
mlir/lib/Dialect/LLVMIR/IR/NVVMDialect.cpp | 20 ++++++-------
.../Target/LLVMIR/nvvm/convert_fp16x2.mlir | 28 +++++++++++++++++++
.../LLVMIR/nvvm/invalid_convert_fp16x2.mlir | 16 +++++++++++
4 files changed, 61 insertions(+), 13 deletions(-)
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
+}
More information about the Mlir-commits
mailing list