[mlir][arith] Add nneg to extui and uitofp. (#183165)
This patchset adds missing the missing flag nneg (non-negative) to extui and uitofp which denotes that the operand is known to be non-negative. Semantics for this flag mirrors LLVM semantics. [From:](https://discourse.llvm.org/t/rfc-add-zext-nneg-flag/73914) > If the nneg flag is set, and the zext argument is negative, the result is a poison value. > A corollary is that replacing a zext nneg with sext is a refinement. [and](https://discourse.llvm.org/t/rfc-support-nneg-flag-with-uitofp/77988): > uitofp nneg iN %x to fM returns poison if %x is negative > A corollary is that uitofp nneg iN %x to fM is equivilent to sitofp iN %x to fM. * Adds ArithNonNegFlagInterface * Adds AttrConvertNonNegToLLVM * Updates definitions of arith.extui and arith.uitofp to declare ArithNonNegFlagInterface * Updates canonicalization patterns to propagate nneg where applicable * Adds roundtrip, lowering, and canonicalization tests. Assisted-by: claude
This commit is contained in:
parent
09d7b890e0
commit
81afd93a51
@ -111,6 +111,29 @@ private:
|
||||
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"))
|
||||
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; }
|
||||
|
||||
private:
|
||||
NamedAttrList convertedAttr;
|
||||
DictionaryAttr propertiesAttr;
|
||||
};
|
||||
|
||||
template <typename SourceOp, typename TargetOp>
|
||||
class AttrConverterConstrainedFPToLLVM {
|
||||
static_assert(TargetOp::template hasTrait<
|
||||
|
||||
@ -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,11 @@ 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.
|
||||
|
||||
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:
|
||||
|
||||
```mlir
|
||||
@ -1189,9 +1195,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 +1491,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.
|
||||
|
||||
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:
|
||||
|
||||
```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;
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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>;
|
||||
|
||||
|
||||
@ -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)]>;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@ -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>)>
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user