[Mlir-commits] [mlir] [mlir][arith] Add nneg to extui and uitofp. (PR #183165)
Erick Ochoa Lopez
llvmlistbot at llvm.org
Wed Feb 25 08:31:41 PST 2026
https://github.com/amd-eochoalo updated https://github.com/llvm/llvm-project/pull/183165
>From 4d2a02318410daf498df1cd07d8c25fb05b82bfc Mon Sep 17 00:00:00 2001
From: Erick Ochoa <erick.ochoalopez at amd.com>
Date: Tue, 24 Feb 2026 13:54:11 -0500
Subject: [PATCH 1/8] [mlir][arith] Add ArithNonNegFlagInterface
---
.../Dialect/Arith/IR/ArithOpsInterfaces.td | 47 +++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/mlir/include/mlir/Dialect/Arith/IR/ArithOpsInterfaces.td b/mlir/include/mlir/Dialect/Arith/IR/ArithOpsInterfaces.td
index 82d6c9ad6b03d..d1b8e250cdb59 100644
--- a/mlir/include/mlir/Dialect/Arith/IR/ArithOpsInterfaces.td
+++ b/mlir/include/mlir/Dialect/Arith/IR/ArithOpsInterfaces.td
@@ -106,6 +106,53 @@ def ArithIntegerOverflowFlagsInterface : OpInterface<"ArithIntegerOverflowFlagsI
];
}
+def ArithNonNegFlagInterface : OpInterface<"ArithNonNegFlagInterface"> {
+ let description = [{
+ Access to op non-negative (nneg) flag.
+ }];
+
+ let cppNamespace = "::mlir::arith";
+
+ let methods = [
+ InterfaceMethod<
+ /*desc=*/ "Returns whether the operation has the nneg flag set",
+ /*returnType=*/ "bool",
+ /*methodName=*/ "getNonNeg",
+ /*args=*/ (ins),
+ /*methodBody=*/ [{}],
+ /*defaultImpl=*/ [{
+ auto op = cast<ConcreteOp>(this->getOperation());
+ return op.getNonNegAttr() != nullptr;
+ }]
+ >,
+ InterfaceMethod<
+ /*desc=*/ "Set the nneg flag for the operation",
+ /*returnType=*/ "void",
+ /*methodName=*/ "setNonNeg",
+ /*args=*/ (ins "bool":$nonNeg),
+ /*methodBody=*/ [{}],
+ /*defaultImpl=*/ [{
+ auto op = cast<ConcreteOp>(this->getOperation());
+ if (nonNeg)
+ op.setNonNegAttr(UnitAttr::get(op->getContext()));
+ else
+ op.removeNonNegAttr();
+ }]
+ >,
+ StaticInterfaceMethod<
+ /*desc=*/ [{Returns the name of the NonNeg flag attribute for
+ the operation}],
+ /*returnType=*/ "StringRef",
+ /*methodName=*/ "getNonNegFlagAttrName",
+ /*args=*/ (ins),
+ /*methodBody=*/ [{}],
+ /*defaultImpl=*/ [{
+ return "nonNeg";
+ }]
+ >
+ ];
+}
+
def ArithRoundingModeInterface : OpInterface<"ArithRoundingModeInterface"> {
let description = [{
Access to op rounding mode.
>From 9ffe6faaf14290d1357d62bf52a4326249b05fe7 Mon Sep 17 00:00:00 2001
From: Erick Ochoa <erick.ochoalopez at amd.com>
Date: Tue, 24 Feb 2026 14:10:32 -0500
Subject: [PATCH 2/8] [mlir][arith] Add ArithNonNegFlagInterface to extui and
uitofp
---
.../include/mlir/Dialect/Arith/IR/ArithOps.td | 39 ++++++++++++++++++-
1 file changed, 37 insertions(+), 2 deletions(-)
diff --git a/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td b/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
index c8754954c181d..5308c133f9f29 100644
--- a/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
+++ b/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
@@ -1172,7 +1172,8 @@ def Arith_RemFOp : Arith_FloatBinaryOp<"remf"> {
// ExtUIOp
//===----------------------------------------------------------------------===//
-def Arith_ExtUIOp : Arith_IToICastOp<"extui"> {
+def Arith_ExtUIOp :
+ Arith_IToICastOp<"extui", [DeclareOpInterfaceMethods<ArithNonNegFlagInterface>]> {
let summary = "integer zero extension operation";
let description = [{
The integer zero extension operation takes an integer input of
@@ -1180,6 +1181,10 @@ def Arith_ExtUIOp : Arith_IToICastOp<"extui"> {
bit-width must be larger than the input bit-width (N > M).
The top-most (N - M) bits of the output are filled with zeros.
+ This op supports an `nneg` flag. If the flag is present, the operand
+ is known to be non-negative, i.e. the sign bit is zero. This allows
+ the zero extension to be treated as a sign extension.
+
Example:
```mlir
@@ -1189,9 +1194,17 @@ def Arith_ExtUIOp : Arith_IToICastOp<"extui"> {
%4 = arith.extui %3 : i3 to i6 // %4 is 0b000010
%5 = arith.extui %0 : vector<2 x i32> to vector<2 x i64>
+
+ // Zero extension with nneg flag.
+ %6 = arith.extui %3 nneg : i3 to i6
```
}];
+ let arguments = (ins SignlessFixedWidthIntegerLike:$in, UnitAttr:$nonNeg);
+ let results = (outs SignlessFixedWidthIntegerLike:$out);
+ let assemblyFormat = [{
+ $in (`nneg` $nonNeg^)? attr-dict `:` type($in) `to` type($out)
+ }];
let hasFolder = 1;
let hasVerifier = 1;
}
@@ -1477,13 +1490,35 @@ def Arith_ScalingTruncFOp
// UIToFPOp
//===----------------------------------------------------------------------===//
-def Arith_UIToFPOp : Arith_IToFCastOp<"uitofp"> {
+def Arith_UIToFPOp :
+ Arith_IToFCastOp<"uitofp", [DeclareOpInterfaceMethods<ArithNonNegFlagInterface>]> {
let summary = "cast from unsigned integer type to floating-point";
let description = [{
Cast from a value interpreted as unsigned integer to the corresponding
floating-point value. If the value cannot be exactly represented, it is
rounded using the default rounding mode. When operating on vectors, casts
elementwise.
+
+ This op supports an `nneg` flag. If the flag is present, the operand
+ is known to be non-negative, i.e. the sign bit is zero. This allows
+ the unsigned-to-float conversion to be treated as a signed-to-float
+ conversion.
+
+ Example:
+
+ ```mlir
+ // Without nneg flag.
+ %0 = arith.uitofp %a : i32 to f64
+
+ // With nneg flag.
+ %1 = arith.uitofp %a nneg : i32 to f64
+ ```
+ }];
+
+ let arguments = (ins SignlessFixedWidthIntegerLike:$in, UnitAttr:$nonNeg);
+ let results = (outs FloatLike:$out);
+ let assemblyFormat = [{
+ $in (`nneg` $nonNeg^)? attr-dict `:` type($in) `to` type($out)
}];
let hasFolder = 1;
}
>From 4384b7930aec80f000b1e888200a56b259b0e52d Mon Sep 17 00:00:00 2001
From: Erick Ochoa <erick.ochoalopez at amd.com>
Date: Tue, 24 Feb 2026 14:12:30 -0500
Subject: [PATCH 3/8] [mlir][arith] Add AttrConvertNonNegToLLVM
---
.../ArithCommon/AttrToLLVMConverter.h | 23 +++++++++++++++++++
.../Conversion/ArithToLLVM/ArithToLLVM.cpp | 5 ++--
2 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h b/mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h
index 7020e24517d09..09e3c7b454764 100644
--- a/mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h
+++ b/mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h
@@ -111,6 +111,29 @@ class AttrConvertOverflowToLLVM {
DictionaryAttr propertiesAttr;
};
+// Attribute converter that populates a NamedAttrList by removing the nonNeg
+// attribute from the source operation attributes, and setting it as a property
+// on the target LLVM operation.
+template <typename SourceOp, typename TargetOp>
+class AttrConvertNonNegToLLVM {
+public:
+ AttrConvertNonNegToLLVM(SourceOp srcOp) {
+ convertedAttr = NamedAttrList{srcOp->getAttrs()};
+ if (convertedAttr.erase("nonNeg")) {
+ MLIRContext *ctx = srcOp.getOperation()->getContext();
+ Builder b(ctx);
+ NamedAttribute attr{"nonNeg", b.getUnitAttr()};
+ propertiesAttr = b.getDictionaryAttr(ArrayRef(attr));
+ }
+ }
+ ArrayRef<NamedAttribute> getAttrs() const { return convertedAttr.getAttrs(); }
+ Attribute getPropAttr() const { return propertiesAttr; }
+
+private:
+ NamedAttrList convertedAttr;
+ DictionaryAttr propertiesAttr;
+};
+
template <typename SourceOp, typename TargetOp>
class AttrConverterConstrainedFPToLLVM {
static_assert(TargetOp::template hasTrait<
diff --git a/mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp b/mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp
index 220826dc5f3ac..178dcd419264d 100644
--- a/mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp
+++ b/mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp
@@ -104,7 +104,8 @@ using ExtFOpLowering = VectorConvertToLLVMPattern<arith::ExtFOp, LLVM::FPExtOp,
using ExtSIOpLowering =
VectorConvertToLLVMPattern<arith::ExtSIOp, LLVM::SExtOp>;
using ExtUIOpLowering =
- VectorConvertToLLVMPattern<arith::ExtUIOp, LLVM::ZExtOp>;
+ VectorConvertToLLVMPattern<arith::ExtUIOp, LLVM::ZExtOp,
+ arith::AttrConvertNonNegToLLVM>;
using FPToSIOpLowering =
VectorConvertToLLVMPattern<arith::FPToSIOp, LLVM::FPToSIOp,
AttrConvertPassThrough,
@@ -187,7 +188,7 @@ using TruncIOpLowering =
arith::AttrConvertOverflowToLLVM>;
using UIToFPOpLowering =
VectorConvertToLLVMPattern<arith::UIToFPOp, LLVM::UIToFPOp,
- AttrConvertPassThrough,
+ arith::AttrConvertNonNegToLLVM,
/*FailOnUnsupportedFP=*/true>;
using XOrIOpLowering = VectorConvertToLLVMPattern<arith::XOrIOp, LLVM::XOrOp>;
>From ee196a7d6fd4246f0e123d87e2ccfc002759c261 Mon Sep 17 00:00:00 2001
From: Erick Ochoa <erick.ochoalopez at amd.com>
Date: Tue, 24 Feb 2026 14:46:15 -0500
Subject: [PATCH 4/8] [mlir][arith] Roundtrip tests for nneg
---
mlir/test/Dialect/Arith/ops.mlir | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/mlir/test/Dialect/Arith/ops.mlir b/mlir/test/Dialect/Arith/ops.mlir
index 58eadfda17060..9541cecb489f6 100644
--- a/mlir/test/Dialect/Arith/ops.mlir
+++ b/mlir/test/Dialect/Arith/ops.mlir
@@ -625,6 +625,20 @@ func.func @test_extui_scalable_vector(%arg0 : vector<[8]xi32>) -> vector<[8]xi64
return %0 : vector<[8]xi64>
}
+// CHECK-LABEL: test_extui_nneg
+// CHECK: arith.extui %{{.*}} nneg : i32 to i64
+func.func @test_extui_nneg(%arg0 : i32) -> i64 {
+ %0 = arith.extui %arg0 nneg : i32 to i64
+ return %0 : i64
+}
+
+// CHECK-LABEL: test_extui_nneg_vector
+// CHECK: arith.extui %{{.*}} nneg : vector<8xi32> to vector<8xi64>
+func.func @test_extui_nneg_vector(%arg0 : vector<8xi32>) -> vector<8xi64> {
+ %0 = arith.extui %arg0 nneg : vector<8xi32> to vector<8xi64>
+ return %0 : vector<8xi64>
+}
+
// CHECK-LABEL: test_extsi
func.func @test_extsi(%arg0 : i32) -> i64 {
%0 = arith.extsi %arg0 : i32 to i64
@@ -761,6 +775,20 @@ func.func @test_uitofp_scalable_vector(%arg0 : vector<[8]xi32>) -> vector<[8]xf3
return %0 : vector<[8]xf32>
}
+// CHECK-LABEL: test_uitofp_nneg
+// CHECK: arith.uitofp %{{.*}} nneg : i32 to f32
+func.func @test_uitofp_nneg(%arg0 : i32) -> f32 {
+ %0 = arith.uitofp %arg0 nneg : i32 to f32
+ return %0 : f32
+}
+
+// CHECK-LABEL: test_uitofp_nneg_vector
+// CHECK: arith.uitofp %{{.*}} nneg : vector<8xi32> to vector<8xf32>
+func.func @test_uitofp_nneg_vector(%arg0 : vector<8xi32>) -> vector<8xf32> {
+ %0 = arith.uitofp %arg0 nneg : vector<8xi32> to vector<8xf32>
+ return %0 : vector<8xf32>
+}
+
// CHECK-LABEL: test_sitofp
func.func @test_sitofp(%arg0 : i16) -> f64 {
%0 = arith.sitofp %arg0 : i16 to f64
>From 43719c8a77a7f0307edb9fded45ff7c81ae94433 Mon Sep 17 00:00:00 2001
From: Erick Ochoa <erick.ochoalopez at amd.com>
Date: Tue, 24 Feb 2026 14:47:38 -0500
Subject: [PATCH 5/8] [mlir] Add tests for nneg in arith-to-llvm
---
.../Conversion/ArithToLLVM/arith-to-llvm.mlir | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir b/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir
index b53c52d75c0aa..7ae27a884d5d3 100644
--- a/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir
+++ b/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir
@@ -754,6 +754,30 @@ func.func @ops_supporting_exact(i32, i32) {
// -----
+// CHECK-LABEL: @ops_supporting_nneg
+func.func @ops_supporting_nneg(%arg0: i32) {
+ // CHECK: llvm.zext nneg %{{.*}} : i32 to i64
+ %0 = arith.extui %arg0 nneg : i32 to i64
+ // CHECK: llvm.uitofp nneg %{{.*}} : i32 to f32
+ %1 = arith.uitofp %arg0 nneg : i32 to f32
+ return
+}
+
+// -----
+
+// CHECK-LABEL: @ops_nneg_not_set
+func.func @ops_nneg_not_set(%arg0: i32) {
+ // CHECK: llvm.zext %{{.*}} : i32 to i64
+ // CHECK-NOT: nneg
+ %0 = arith.extui %arg0 : i32 to i64
+ // CHECK: llvm.uitofp %{{.*}} : i32 to f32
+ // CHECK-NOT: nneg
+ %1 = arith.uitofp %arg0 : i32 to f32
+ return
+}
+
+// -----
+
// CHECK-LABEL: func @memref_bitcast
// CHECK-SAME: (%[[ARG:.*]]: memref<?xi16>)
// CHECK: %[[V1:.*]] = builtin.unrealized_conversion_cast %[[ARG]] : memref<?xi16> to !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
>From 7b9f4fe075e8ad4238d1327622d8de3735161d1c Mon Sep 17 00:00:00 2001
From: Erick Ochoa <erick.ochoalopez at amd.com>
Date: Tue, 24 Feb 2026 15:33:26 -0500
Subject: [PATCH 6/8] [mlir][arith] Canonicalization with nneg
---
.../Dialect/Arith/IR/ArithCanonicalization.td | 38 ++++--
mlir/test/Dialect/Arith/canonicalize.mlir | 127 ++++++++++++++++++
2 files changed, 152 insertions(+), 13 deletions(-)
diff --git a/mlir/lib/Dialect/Arith/IR/ArithCanonicalization.td b/mlir/lib/Dialect/Arith/IR/ArithCanonicalization.td
index e256915933a71..8be2af5cb3cfc 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithCanonicalization.td
+++ b/mlir/lib/Dialect/Arith/IR/ArithCanonicalization.td
@@ -218,8 +218,12 @@ def XOrINotCmpI :
(Arith_CmpIOp (InvertPredicate $pred), $a, $b)>;
// xor extui(x), extui(y) -> extui(xor(x,y))
+// XOR preserves nneg only if both operands are non-negative: xor of two values
+// with sign bit 0 has sign bit 0.
def XOrIOfExtUI :
- Pat<(Arith_XOrIOp (Arith_ExtUIOp $x), (Arith_ExtUIOp $y)), (Arith_ExtUIOp (Arith_XOrIOp $x, $y)),
+ Pat<(Arith_XOrIOp (Arith_ExtUIOp $x, $nneg1), (Arith_ExtUIOp $y, $nneg2)),
+ (Arith_ExtUIOp (Arith_XOrIOp $x, $y),
+ (NativeCodeCall<"($0 && $1) ? $0 : UnitAttr()"> $nneg1, $nneg2)),
[(Constraint<CPred<"$0.getType() == $1.getType()">> $x, $y)]>;
// xor extsi(x), extsi(y) -> extsi(xor(x,y))
@@ -245,8 +249,8 @@ def CmpIExtSI :
// cmpi(== or !=, a ext iNN, b ext iNN) == cmpi(== or !=, a, b)
def CmpIExtUI :
Pat<(Arith_CmpIOp $pred,
- (Arith_ExtUIOp $a),
- (Arith_ExtUIOp $b)),
+ (Arith_ExtUIOp $a, $nneg1),
+ (Arith_ExtUIOp $b, $nneg2)),
(Arith_CmpIOp $pred, $a, $b),
[(Constraint<CPred<"$0.getType() == $1.getType()">> $a, $b),
(Constraint<
@@ -306,7 +310,8 @@ def IndexCastUIOfIndexCastUI :
// index_castui(extui(x)) -> index_castui(x)
def IndexCastUIOfExtUI :
- Pat<(Arith_IndexCastUIOp (Arith_ExtUIOp $x)), (Arith_IndexCastUIOp $x)>;
+ Pat<(Arith_IndexCastUIOp (Arith_ExtUIOp $x, $nneg)),
+ (Arith_IndexCastUIOp $x)>;
//===----------------------------------------------------------------------===//
@@ -323,16 +328,20 @@ def BitcastOfBitcast :
// extsi(extui(x iN : iM) : iL) -> extui(x : iL)
def ExtSIOfExtUI :
- Pat<(Arith_ExtSIOp (Arith_ExtUIOp $x)), (Arith_ExtUIOp $x)>;
+ Pat<(Arith_ExtSIOp (Arith_ExtUIOp $x, $nneg)),
+ (Arith_ExtUIOp $x, $nneg)>;
//===----------------------------------------------------------------------===//
// AndIOp
//===----------------------------------------------------------------------===//
// and extui(x), extui(y) -> extui(and(x,y))
+// AND can only clear bits, so if either operand is non-negative (sign bit 0),
+// the result also has sign bit 0. Preserves nneg if either operand has it.
def AndOfExtUI :
- Pat<(Arith_AndIOp (Arith_ExtUIOp $x), (Arith_ExtUIOp $y)),
- (Arith_ExtUIOp (Arith_AndIOp $x, $y)),
+ Pat<(Arith_AndIOp (Arith_ExtUIOp $x, $nneg1), (Arith_ExtUIOp $y, $nneg2)),
+ (Arith_ExtUIOp (Arith_AndIOp $x, $y),
+ (NativeCodeCall<"$0 ? $0 : $1"> $nneg1, $nneg2)),
[(Constraint<CPred<"$0.getType() == $1.getType()">> $x, $y)]>;
// and extsi(x), extsi(y) -> extsi(and(x,y))
@@ -346,9 +355,12 @@ def AndOfExtSI :
//===----------------------------------------------------------------------===//
// or extui(x), extui(y) -> extui(or(x,y))
+// OR preserves nneg only if both operands are non-negative: or of two values
+// with sign bit 0 has sign bit 0.
def OrOfExtUI :
- Pat<(Arith_OrIOp (Arith_ExtUIOp $x), (Arith_ExtUIOp $y)),
- (Arith_ExtUIOp (Arith_OrIOp $x, $y)),
+ Pat<(Arith_OrIOp (Arith_ExtUIOp $x, $nneg1), (Arith_ExtUIOp $y, $nneg2)),
+ (Arith_ExtUIOp (Arith_OrIOp $x, $y),
+ (NativeCodeCall<"($0 && $1) ? $0 : UnitAttr()"> $nneg1, $nneg2)),
[(Constraint<CPred<"$0.getType() == $1.getType()">> $x, $y)]>;
// or extsi(x), extsi(y) -> extsi(or(x,y))
@@ -381,8 +393,8 @@ def TruncIExtSIToExtSI :
// trunci(extui(x)) -> extui(x), when only the zero-extension bits are truncated
def TruncIExtUIToExtUI :
- Pat<(Arith_TruncIOp:$tr (Arith_ExtUIOp:$ext $x), $overflow),
- (Arith_ExtUIOp $x),
+ Pat<(Arith_TruncIOp:$tr (Arith_ExtUIOp:$ext $x, $nneg), $overflow),
+ (Arith_ExtUIOp $x, $nneg),
[(ValueWiderThan $ext, $tr),
(ValueWiderThan $tr, $x)]>;
@@ -405,8 +417,8 @@ def TruncFSIToFPToSIToFP :
// truncf(uitofp(x)) -> uitofp(x) if default rounding mode.
def TruncFUIToFPToUIToFP :
- Pat<(Arith_TruncFOp:$tr (Arith_UIToFPOp:$fp $x), $rmf, $fmf),
- (Arith_UIToFPOp $x),
+ Pat<(Arith_TruncFOp:$tr (Arith_UIToFPOp:$fp $x, $nneg), $rmf, $fmf),
+ (Arith_UIToFPOp $x, $nneg),
[(Constraint<CPred<"$0 == nullptr">, "default rounding mode"> $rmf)]>;
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/Arith/canonicalize.mlir b/mlir/test/Dialect/Arith/canonicalize.mlir
index 18e0d2d2ea3c4..52f4a54feebab 100644
--- a/mlir/test/Dialect/Arith/canonicalize.mlir
+++ b/mlir/test/Dialect/Arith/canonicalize.mlir
@@ -366,6 +366,15 @@ func.func @extSIOfExtUI(%arg0: i1) -> i64 {
return %ext2 : i64
}
+// CHECK-LABEL: @extSIOfExtUI_nneg
+// CHECK: %[[res:.+]] = arith.extui %arg0 nneg : i1 to i64
+// CHECK: return %[[res]]
+func.func @extSIOfExtUI_nneg(%arg0: i1) -> i64 {
+ %ext1 = arith.extui %arg0 nneg : i1 to i8
+ %ext2 = arith.extsi %ext1 : i8 to i64
+ return %ext2 : i64
+}
+
// CHECK-LABEL: @extUIOfExtUI
// CHECK: %[[res:.+]] = arith.extui %arg0 : i1 to i64
// CHECK: return %[[res]]
@@ -490,6 +499,39 @@ func.func @andOfExtUI(%arg0: i8, %arg1: i8) -> i64 {
return %res : i64
}
+// CHECK-LABEL: @andOfExtUI_nneg_lhs
+// CHECK: %[[comb:.+]] = arith.andi %arg0, %arg1 : i8
+// CHECK: %[[ext:.+]] = arith.extui %[[comb]] nneg : i8 to i64
+// CHECK: return %[[ext]]
+func.func @andOfExtUI_nneg_lhs(%arg0: i8, %arg1: i8) -> i64 {
+ %ext0 = arith.extui %arg0 nneg : i8 to i64
+ %ext1 = arith.extui %arg1 : i8 to i64
+ %res = arith.andi %ext0, %ext1 : i64
+ return %res : i64
+}
+
+// CHECK-LABEL: @andOfExtUI_nneg_rhs
+// CHECK: %[[comb:.+]] = arith.andi %arg0, %arg1 : i8
+// CHECK: %[[ext:.+]] = arith.extui %[[comb]] nneg : i8 to i64
+// CHECK: return %[[ext]]
+func.func @andOfExtUI_nneg_rhs(%arg0: i8, %arg1: i8) -> i64 {
+ %ext0 = arith.extui %arg0 : i8 to i64
+ %ext1 = arith.extui %arg1 nneg : i8 to i64
+ %res = arith.andi %ext0, %ext1 : i64
+ return %res : i64
+}
+
+// CHECK-LABEL: @andOfExtUI_nneg_both
+// CHECK: %[[comb:.+]] = arith.andi %arg0, %arg1 : i8
+// CHECK: %[[ext:.+]] = arith.extui %[[comb]] nneg : i8 to i64
+// CHECK: return %[[ext]]
+func.func @andOfExtUI_nneg_both(%arg0: i8, %arg1: i8) -> i64 {
+ %ext0 = arith.extui %arg0 nneg : i8 to i64
+ %ext1 = arith.extui %arg1 nneg : i8 to i64
+ %res = arith.andi %ext0, %ext1 : i64
+ return %res : i64
+}
+
// CHECK-LABEL: @orOfExtSI
// CHECK: %[[comb:.+]] = arith.ori %arg0, %arg1 : i8
// CHECK: %[[ext:.+]] = arith.extsi %[[comb]] : i8 to i64
@@ -512,6 +554,29 @@ func.func @orOfExtUI(%arg0: i8, %arg1: i8) -> i64 {
return %res : i64
}
+// CHECK-LABEL: @orOfExtUI_nneg_both
+// CHECK: %[[comb:.+]] = arith.ori %arg0, %arg1 : i8
+// CHECK: %[[ext:.+]] = arith.extui %[[comb]] nneg : i8 to i64
+// CHECK: return %[[ext]]
+func.func @orOfExtUI_nneg_both(%arg0: i8, %arg1: i8) -> i64 {
+ %ext0 = arith.extui %arg0 nneg : i8 to i64
+ %ext1 = arith.extui %arg1 nneg : i8 to i64
+ %res = arith.ori %ext0, %ext1 : i64
+ return %res : i64
+}
+
+// CHECK-LABEL: @orOfExtUI_nneg_mixed
+// CHECK: %[[comb:.+]] = arith.ori %arg0, %arg1 : i8
+// CHECK: %[[ext:.+]] = arith.extui %[[comb]] : i8 to i64
+// CHECK-NOT: nneg
+// CHECK: return %[[ext]]
+func.func @orOfExtUI_nneg_mixed(%arg0: i8, %arg1: i8) -> i64 {
+ %ext0 = arith.extui %arg0 nneg : i8 to i64
+ %ext1 = arith.extui %arg1 : i8 to i64
+ %res = arith.ori %ext0, %ext1 : i64
+ return %res : i64
+}
+
// -----
// CHECK-LABEL: @indexCastOfSignExtend
@@ -532,6 +597,15 @@ func.func @indexCastUIOfUnsignedExtend(%arg0: i8) -> index {
return %idx : index
}
+// CHECK-LABEL: @indexCastUIOfUnsignedExtend_nneg
+// CHECK: %[[res:.+]] = arith.index_castui %arg0 : i8 to index
+// CHECK: return %[[res]]
+func.func @indexCastUIOfUnsignedExtend_nneg(%arg0: i8) -> index {
+ %ext = arith.extui %arg0 nneg : i8 to i16
+ %idx = arith.index_castui %ext : i16 to index
+ return %idx : index
+}
+
// CHECK-LABEL: @indexCastFold
// CHECK: %[[res:.*]] = arith.constant -2 : index
// CHECK: return %[[res]]
@@ -771,6 +845,26 @@ func.func @truncSitofpConstrained(%arg0: i32) -> f32 {
return %trunc : f32
}
+// CHECK-LABEL: @truncUitofp
+// CHECK: %[[UITOFP:.*]] = arith.uitofp %[[ARG0:.*]] : i32 to f32
+// CHECK-NOT: truncf
+// CHECK: return %[[UITOFP]]
+func.func @truncUitofp(%arg0: i32) -> f32 {
+ %uitofp = arith.uitofp %arg0 : i32 to f64
+ %trunc = arith.truncf %uitofp : f64 to f32
+ return %trunc : f32
+}
+
+// CHECK-LABEL: @truncUitofp_nneg
+// CHECK: %[[UITOFP:.*]] = arith.uitofp %[[ARG0:.*]] nneg : i32 to f32
+// CHECK-NOT: truncf
+// CHECK: return %[[UITOFP]]
+func.func @truncUitofp_nneg(%arg0: i32) -> f32 {
+ %uitofp = arith.uitofp %arg0 nneg : i32 to f64
+ %trunc = arith.truncf %uitofp : f64 to f32
+ return %trunc : f32
+}
+
// TODO: We should also add a test for not folding arith.extf on information loss.
// This may happen when extending f8E5M2FNUZ to f16.
@@ -812,6 +906,16 @@ func.func @truncExtui3(%arg0: i8) -> i16 {
return %trunci : i16
}
+// CHECK-LABEL: @truncExtui3_nneg
+// CHECK: %[[ARG0:.+]]: i8
+// CHECK: %[[CST:.*]] = arith.extui %[[ARG0:.+]] nneg : i8 to i16
+// CHECK: return %[[CST:.*]] : i16
+func.func @truncExtui3_nneg(%arg0: i8) -> i16 {
+ %extui = arith.extui %arg0 nneg : i8 to i32
+ %trunci = arith.trunci %extui : i32 to i16
+ return %trunci : i16
+}
+
// CHECK-LABEL: @truncExtuiVector
// CHECK: %[[ARG0:.+]]: vector<2xi32>
// CHECK: %[[CST:.*]] = arith.trunci %[[ARG0:.+]] : vector<2xi32> to vector<2xi16>
@@ -1817,6 +1921,29 @@ func.func @xorOfExtUI(%arg0: i8, %arg1: i8) -> i64 {
return %res : i64
}
+// CHECK-LABEL: @xorOfExtUI_nneg_both
+// CHECK: %[[comb:.+]] = arith.xori %arg0, %arg1 : i8
+// CHECK: %[[ext:.+]] = arith.extui %[[comb]] nneg : i8 to i64
+// CHECK: return %[[ext]]
+func.func @xorOfExtUI_nneg_both(%arg0: i8, %arg1: i8) -> i64 {
+ %ext0 = arith.extui %arg0 nneg : i8 to i64
+ %ext1 = arith.extui %arg1 nneg : i8 to i64
+ %res = arith.xori %ext0, %ext1 : i64
+ return %res : i64
+}
+
+// CHECK-LABEL: @xorOfExtUI_nneg_mixed
+// CHECK: %[[comb:.+]] = arith.xori %arg0, %arg1 : i8
+// CHECK: %[[ext:.+]] = arith.extui %[[comb]] : i8 to i64
+// CHECK-NOT: nneg
+// CHECK: return %[[ext]]
+func.func @xorOfExtUI_nneg_mixed(%arg0: i8, %arg1: i8) -> i64 {
+ %ext0 = arith.extui %arg0 nneg : i8 to i64
+ %ext1 = arith.extui %arg1 : i8 to i64
+ %res = arith.xori %ext0, %ext1 : i64
+ return %res : i64
+}
+
// -----
// CHECK-LABEL: @bitcastSameType(
>From 5aaa1b42584c19322600edc245a564fe88f07292 Mon Sep 17 00:00:00 2001
From: Erick Ochoa <erick.ochoalopez at amd.com>
Date: Wed, 25 Feb 2026 11:28:36 -0500
Subject: [PATCH 7/8] Exit early
---
.../Conversion/ArithCommon/AttrToLLVMConverter.h | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h b/mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h
index 09e3c7b454764..fccfe4897114e 100644
--- a/mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h
+++ b/mlir/include/mlir/Conversion/ArithCommon/AttrToLLVMConverter.h
@@ -119,12 +119,12 @@ class AttrConvertNonNegToLLVM {
public:
AttrConvertNonNegToLLVM(SourceOp srcOp) {
convertedAttr = NamedAttrList{srcOp->getAttrs()};
- if (convertedAttr.erase("nonNeg")) {
- MLIRContext *ctx = srcOp.getOperation()->getContext();
- Builder b(ctx);
- NamedAttribute attr{"nonNeg", b.getUnitAttr()};
- propertiesAttr = b.getDictionaryAttr(ArrayRef(attr));
- }
+ if (!convertedAttr.erase("nonNeg"))
+ return;
+ MLIRContext *ctx = srcOp.getOperation()->getContext();
+ Builder b(ctx);
+ NamedAttribute attr{"nonNeg", b.getUnitAttr()};
+ propertiesAttr = b.getDictionaryAttr(ArrayRef(attr));
}
ArrayRef<NamedAttribute> getAttrs() const { return convertedAttr.getAttrs(); }
Attribute getPropAttr() const { return propertiesAttr; }
>From d5f10d9be357e252954797ccf6b94822f0f4ecdc Mon Sep 17 00:00:00 2001
From: Erick Ochoa <erick.ochoalopez at amd.com>
Date: Wed, 25 Feb 2026 11:31:18 -0500
Subject: [PATCH 8/8] Update descriptions
---
mlir/include/mlir/Dialect/Arith/IR/ArithOps.td | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td b/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
index 5308c133f9f29..c372038a8d43e 100644
--- a/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
+++ b/mlir/include/mlir/Dialect/Arith/IR/ArithOps.td
@@ -1181,9 +1181,10 @@ def Arith_ExtUIOp :
bit-width must be larger than the input bit-width (N > M).
The top-most (N - M) bits of the output are filled with zeros.
- This op supports an `nneg` flag. If the flag is present, the operand
- is known to be non-negative, i.e. the sign bit is zero. This allows
- the zero extension to be treated as a sign extension.
+ When the `nneg` flag is present, the operand is assumed to have
+ the most significant bit set to 0. In this case, zero extension is
+ equivalent to sign extension. When this assumption is violated, the
+ result is poison.
Example:
@@ -1499,10 +1500,10 @@ def Arith_UIToFPOp :
rounded using the default rounding mode. When operating on vectors, casts
elementwise.
- This op supports an `nneg` flag. If the flag is present, the operand
- is known to be non-negative, i.e. the sign bit is zero. This allows
- the unsigned-to-float conversion to be treated as a signed-to-float
- conversion.
+ When the `nneg` flag is present, the operand is assumed to have
+ the most significant bit set to 0. In this case, zero extension is
+ equivalent to sign extension. When this assumption is violated, the
+ result is poison.
Example:
More information about the Mlir-commits
mailing list